[wpa_supplicant] Cumulative patch from commit 61c6e7c62
Bug: 245752074
Test: connect/disconnect to WPA2, WPA3 networks
Test: SoftAp & p2p connection
Test: Regression test(b/247129565)
BYPASS_INCLUSIVE_LANGUAGE_REASON=Merged from Open source
61c6e7c62 nl80211: Parsing of MLO connection info from roam+auth event
3d842d910 MLD STA: Add support for parsing MLO KDEs
e1105bab8 Add QCA vendor interface support for Spatial Reuse (SR) feature
1f39f85b7 Fix compiler warning on shift overflow in QCA vendor definitions
c5b950b6a Fix compilation error due to use of bitwise '&' with boolean operands
7bdd3f2aa P2P: Allow P2P CSA from 5 GHz to 2.4 GHz band
a7684a21c Update hw mode after ACS selects the channel
768537921 P2P: Set operating class along with operating channel width
ee7eec518 P2P: Fix the issue in setting optimized listen channel
01944c095 Fix RNR BSSID setting for own interfaces
3a7fe1e21 dbus: Omit FT key mgmt capabilities without CONFIG_IEEE80211R
d2caf6ef5 dbus: Fix property DebugShowKeys and DebugTimestamp
a17f9a2d4 Add usage print for -q flag
f77c0f914 ACS: Include frequency in info messages
0c7b3814c Use a less generic name for IEEE802.11 CRC-32 routine
7ed17eee3 ACS: Don't select indoor channel on outdoor operation
1f795df7a wpa_supplicant man page missing -I flag
ef2d2e81a Add a new QCA vendor attribute to support flexible TWT
1b6f3b585 MLD STA: Indicate per link channel switch
b7f98d92d MLD STA: Add per-link MLO signal poll
28b2256da MLD STA: Add MLO_STATUS control interface command
8dd5b9a9e nl80211: Send bssid info as parameter to nl80211_get_link_signal()
6ca98040a MLD STA: Indicate AP MLD address in STATUS command
22ba81cfe MLD STA: Indicate AP MLD address in CTRL-EVENT-CONNECTED
db99e7341 Add AP MLD address into BSS command output
4bd316524 bss: Parse and store MLD address of the AP from Multi-Link element
cc29cadf2 Helper function to get MLD address from Basic Multi-Link element
9a8bd7be0 scan: Helper function to get ML IE of specified type from scan result IEs
e21128614 nl80211: Use AP MLD address to set supplicant port as authorized
7784964cb MLD STA: Fetch MLO connection info into core wpa_supplicant
e2147f917 nl80211: Indicate MLO connection info in driver status
c7741009f nl80211: Parse MLO connection info in NL80211_CMD_CONNECT event
bd499f0e6 Add a helper function to get ML IE of specified type from IEs buffer
36645f839 EHT: Add Multi-Link element field definitions
62612dfc1 P2P: Use only PSC for P2P group in the 6 GHz band
7974d8053 Configure RRM elements to the driver in the driver-AP-SME case
d1a7626ff SAE: Use correct SSID profile for SAE auth retries during external auth
096feac19 SAE: Include the new -EXT-KEY AKMs in external auth case
76793cbbc SAE: Make sure H2E PT is derived for external auth SSID profile
122cdd592 Enable TWT responder AP role only if IEEE 802.11ax/HE is enabled
ed442e8dc Add p2p_optimize_listen_chan=1 to default Android template
5b76c3654 Extend attributes of QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO
897e09085 QCA vendor attribute to configure periodic sounding
0cc6f985d wlantest: Recognize additional not-Robust Action categories
cd392151c Validate MAC Address KDE length in the parser
6f8af5974 Fix expiration logic for the first PTKSA cache entry
85e28a79b PASN: Set secure ranging context to driver after association
9b62b61c6 PASN: Configure secure ranging context to the driver in AP mode
de3b91a17 nl80211: Define vendor interface functions to offload PASN authentication
edd89d6db PASN: Set keys from PTKSA cache for a valid PTKSA entry
e2c3cdf2c nl80211: Register PASN Authentication frames for SME-in-driver
58a96187e nl80211: Allow PASN Authentication frames with random (foreign) MAC address
24929543b PASN: Deauthenticate on PTKSA cache entry expiration
74d894a2e PASN: Offload handshake for multiple peers from the driver
06317f5e3 PASN: Add driver operation to set secure ranging context and PASN response
2edebc6b6 PASN: Add interface to handle PASN request from the driver
9330bf318 PASN: Add function to compute LTF keyseed from KDK
9391f7100 Add own MAC address in PASN supplicant start and deauthentication
580bd04cf Add own MAC address used for key derivation to PTKSA cache
d0d585c48 Store secure ranging driver capabilities in WPA state machine
96a604128 Use separate PASN capabilities for AP and STA modes
909fa448e EAPOL: Update PMK length in EAPOL callback to support longer keys
c80dc6940 OpenSSL: Include rsa.h for all OpenSSL versions
723eb4f38 P2P: Fix a typo in a comment about removing 6 GHz channels
e9627f8c3 P2P: Skip 6 GHz band directly if 6 GHz P2P is disabled
03f7f633a Fix wrong AKM priority for FILS
1f9a988f1 DPP3: Do not initiate PKEX for PB if no configuration is available
aa75aa1dc Add QCA vendor interface to get SAR capabilities to userspace
5de45546d Add support to send multi AKM connect request when driver's SME in use
0ce1545dc nl80211: Determine maximum number of supported AKMs
48c620829 Update PSK after cross AKM roaming to WPA-PSK when driver's SME in use
7e97c619a Sync with wireless-next.git include/uapi/linux/nl80211.h
54706957e DPP: Fix DPP_RELAY_ADD_CONTROLLER command parsing
44b26d82b nl80211: Silence a compiler warning on printf in 32-bit builds
4ae14deee DPP3: Use chirping channel list in PB discovery
c58be1d8f DPP: Channel list generation for presence announcement to helper funcion
820211245 OpenSSL: Fix HPKE in some corner cases
57968faea DPP: Do not discard network introduction frames in test mode
d72302c6b DPP: Do not use 6 GHz channels for push button
89de431f2 DPP: Add config response status value to DPP-CONF-SENT
80d5e264c Enhance QCA vendor roam event to indicate MLO links after reassociation
662249306 Update copyright notices for the QCA vendor definitions
1d08b238c nl80211: Allow more time for the initial scan with 6 GHz
faf9c04cb Remove a host of unnecessary OPENSSL_IS_BORINGSSL ifdefs
b9cd5a82f Always process pending QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH data
ef4cd8e33 QoS: Use common classifier_mask for ipv4/ipv6
93be02592 Add fixed FDD mode to qca_btc_chain_mode QCA vendor attribute
5565fbee2 DPP: Check Enrollee supported curves when building Config Response
4cfb484e9 DPP: Allow dpp_controller_start without arguments in CLIs
c97000933 Fix ifdef condition for imsi_privacy_cert
f70db167a SAE: Derive a variable length PMK with the new AKM suites
91010e6f6 SAE: Indicate AKM suite selector in commit for new AKM suites
e81ec0962 SAE: Use H2E unconditionally with the new AKM suites
f8eed2e8b SAE: Store PMK length and AKM in SAE data
9dc4e9d13 SAE: EAPOL-Key and key/MIC length information for the new AKM suites
a32ef3cfb SAE: Driver capability flags for the new SAE AKM suites
91df8c9c6 SAE: Internal WPA_KEY_MGMT_* defines for extended key AKMs
5c8a714b1 SAE: Use wpa_key_mgmt_sae() helper
5456b0f26 Define new RSN AKM suite selector values
def33101c DPP: Clear push button announcement state on wpa_supplicant FLUSH
d22dfe918 DPP: Event message for indicating when Relay would need a Controller
bfe3cfc38 DPP: Allow Relay connections to Controllers to be added and removed
808834b18 Add a comparison function for hostapd_ip_addr
f7763880b DPP: Advertise Configurator connectivity on Relay automatically
ca682f80a DPP: Dynamic Controller initiated connection on Relay
d2388bcca DPP: Strict validation of PKEX peer bootstrapping key during auth
a7b8cef8b DPP3: Fix push button boostrapping key passing through PKEX
69d7c8e6b DPP: Add peer=id entry for PKEX-over-TCP case
1ff9251a8 DPP3: Push button Configurator in wpa_supplicant
e9137950f DPP: Recognize own PKEX Exchange Request if it ends up being received
692956446 DPP: Note PKEX code/identifier deletion in debug log
ae4a3a6f6 DPP: Add DPP-CONF-REQ-RX event for Controller
fb2937b85 DPP: Allow Controller to initiate PKEX through Relay
15af83cf1 DPP: Delete PKEX code and identifier on success completion of PKEX
479e412a6 DPP3: Default value for dpp_connector_privacy
148de3e0d DPP3: Private Peer Introduction protocol
786ea402b HPKE base mode with single-shot API
f0273bc81 OpenSSL: Remove a forgotten debug print
68209ddbe DPP: Allow 3rd party information to be added into config object
0e2217c95 DPP: Allow 3rd party information to be added into config request obj
3d82fbe05 Add QCA vendor subcommand and attributes for SCS rule configuration
16b62ddfa QCA vendor attribute for DBAM configuration
451ede2c3 DPP: Allow AP/Relay to be configured to listed for new TCP connections
7bbe85987 DPP3: Allow external configuration to be specified on AP for PB
8db786a43 DPP3: Testing functionality for push button announcements
37bccfcab DPP3: Push button bootstrap mechanism
a0054fe7c Add AP and STA specific P802.11az security capabilities (vendor command)
159e63613 QCA vendor command for CoAP offload processing
3b7bb17f6 Add QCA vendor attribute for TIM beacon statistics
Change-Id: Ic5faae10839f317cc70a4df7a3f2047812ffd34c
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 57fc6da..8ee2e04 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -420,19 +420,24 @@
static int acs_survey_is_sufficient(struct freq_survey *survey)
{
if (!(survey->filled & SURVEY_HAS_NF)) {
- wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing noise floor",
+ survey->freq);
return 0;
}
if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
- wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+ wpa_printf(MSG_INFO,
+ "ACS: Survey for freq %d is missing channel time",
+ survey->freq);
return 0;
}
if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
!(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
wpa_printf(MSG_INFO,
- "ACS: Survey is missing RX and busy time (at least one is required)");
+ "ACS: Survey for freq %d is missing RX and busy time (at least one is required)",
+ survey->freq);
return 0;
}
@@ -553,6 +558,10 @@
if (chan->max_tx_power < iface->conf->min_tx_power)
continue;
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
chan->chan, chan->freq);
@@ -687,6 +696,10 @@
if (chan->max_tx_power < iface->conf->min_tx_power)
continue;
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
if (!chan_bw_allowed(chan, bw, 1, 1)) {
wpa_printf(MSG_DEBUG,
"ACS: Channel %d: BW %u is not supported",
@@ -996,7 +1009,8 @@
err = hostapd_select_hw_mode(iface);
if (err) {
- wpa_printf(MSG_ERROR, "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
+ wpa_printf(MSG_ERROR,
+ "ACS: Could not (err: %d) select hw_mode for freq=%d channel=%d",
err, iface->freq, iface->conf->channel);
err = -1;
goto fail;
@@ -1076,6 +1090,10 @@
if (chan->max_tx_power < iface->conf->min_tx_power)
continue;
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
*freq++ = chan->freq;
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index db86a76..b73aae2 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -482,6 +482,7 @@
struct sae_password_entry *pw;
if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
+ !wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt) &&
!hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == 3 ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
@@ -949,6 +950,8 @@
#ifdef CONFIG_DPP
os_free(conf->dpp_name);
os_free(conf->dpp_mud_url);
+ os_free(conf->dpp_extra_conf_req_name);
+ os_free(conf->dpp_extra_conf_req_value);
os_free(conf->dpp_connector);
wpabuf_free(conf->dpp_netaccesskey);
wpabuf_free(conf->dpp_csign);
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 05a1b05..268829a 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -755,12 +755,15 @@
#ifdef CONFIG_DPP
char *dpp_name;
char *dpp_mud_url;
+ char *dpp_extra_conf_req_name;
+ char *dpp_extra_conf_req_value;
char *dpp_connector;
struct wpabuf *dpp_netaccesskey;
unsigned int dpp_netaccesskey_expiry;
struct wpabuf *dpp_csign;
#ifdef CONFIG_DPP2
struct dpp_controller_conf *dpp_controller;
+ int dpp_relay_port;
int dpp_configurator_connectivity;
int dpp_pfs;
#endif /* CONFIG_DPP2 */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
index 87c3b90..b9fd920 100644
--- a/src/ap/ap_drv_ops.c
+++ b/src/ap/ap_drv_ops.c
@@ -75,6 +75,14 @@
*beacon_ret = *proberesp_ret = *assocresp_ret = NULL;
+#ifdef NEED_AP_MLME
+ pos = buf;
+ pos = hostapd_eid_rm_enabled_capab(hapd, pos, sizeof(buf));
+ if (add_buf_data(&assocresp, buf, pos - buf) < 0 ||
+ add_buf_data(&proberesp, buf, pos - buf) < 0)
+ goto fail;
+#endif /* NEED_AP_MLME */
+
pos = buf;
pos = hostapd_eid_time_adv(hapd, pos);
if (add_buf_data(&beacon, buf, pos - buf) < 0)
@@ -1016,3 +1024,30 @@
return 0;
return hapd->driver->dpp_listen(hapd->drv_priv, enable);
}
+
+
+#ifdef CONFIG_PASN
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *peer_addr,
+ u32 cipher, u8 tk_len, const u8 *tk,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action)
+{
+ struct secure_ranging_params params;
+
+ if (!hapd->driver || !hapd->driver->set_secure_ranging_ctx)
+ return 0;
+
+ os_memset(¶ms, 0, sizeof(params));
+ params.own_addr = own_addr;
+ params.peer_addr = peer_addr;
+ params.cipher = cipher;
+ params.tk_len = tk_len;
+ params.tk = tk;
+ params.ltf_keyseed_len = ltf_keyseed_len;
+ params.ltf_keyseed = ltf_keyseed;
+ params.action = action;
+
+ return hapd->driver->set_secure_ranging_ctx(hapd->drv_priv, ¶ms);
+}
+#endif /* CONFIG_PASN */
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index b4fb766..93b2244 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -138,6 +138,11 @@
int hostapd_drv_update_dh_ie(struct hostapd_data *hapd, const u8 *peer,
u16 reason_code, const u8 *ie, size_t ielen);
int hostapd_drv_dpp_listen(struct hostapd_data *hapd, bool enable);
+int hostapd_drv_set_secure_ranging_ctx(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *addr,
+ u32 cipher, u8 key_len, const u8 *key,
+ u8 ltf_keyseed_len,
+ const u8 *ltf_keyseed, u32 action);
#include "drivers/driver.h"
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 29b41f5..ac8f6fa 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1060,7 +1060,8 @@
if (sscanf(pos, "%d", &expiration) != 1)
return NULL;
- return wpa_auth_pmksa_create_entry(aa, spa, pmk, pmkid, expiration);
+ return wpa_auth_pmksa_create_entry(aa, spa, pmk, PMK_LEN,
+ WPA_KEY_MGMT_SAE, pmkid, expiration);
}
#endif /* CONFIG_MESH */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 146dd1a..7f31f28 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -281,6 +281,10 @@
if (chan->max_tx_power < iface->conf->min_tx_power)
continue;
+ if ((chan->flag & HOSTAPD_CHAN_INDOOR_ONLY) &&
+ iface->conf->country[2] == 0x4f)
+ continue;
+
if (ret_chan && idx == channel_idx) {
wpa_printf(MSG_DEBUG, "Selected channel %d (%d)",
chan->freq, chan->chan);
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 2613c0c..32ddb3b 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -15,11 +15,13 @@
#include "common/dpp.h"
#include "common/gas.h"
#include "common/wpa_ctrl.h"
+#include "crypto/random.h"
#include "hostapd.h"
#include "ap_drv_ops.h"
#include "gas_query_ap.h"
#include "gas_serv.h"
#include "wpa_auth.h"
+#include "beacon.h"
#include "dpp_hostapd.h"
@@ -278,19 +280,37 @@
}
+static void hostapd_dpp_pkex_clear_code(struct hostapd_data *hapd)
+{
+ if (!hapd->dpp_pkex_code && !hapd->dpp_pkex_identifier)
+ return;
+
+ /* Delete PKEX code and identifier on successful completion of
+ * PKEX. We are not supposed to reuse these without being
+ * explicitly requested to perform PKEX again. */
+ wpa_printf(MSG_DEBUG, "DPP: Delete PKEX code/identifier");
+ os_free(hapd->dpp_pkex_code);
+ hapd->dpp_pkex_code = NULL;
+ os_free(hapd->dpp_pkex_identifier);
+ hapd->dpp_pkex_identifier = NULL;
+}
+
+
#ifdef CONFIG_DPP2
static int hostapd_dpp_pkex_done(void *ctx, void *conn,
struct dpp_bootstrap_info *peer_bi)
{
struct hostapd_data *hapd = ctx;
- const char *cmd = hapd->dpp_pkex_auth_cmd;
+ char cmd[500];
const char *pos;
u8 allowed_roles = DPP_CAPAB_CONFIGURATOR;
struct dpp_bootstrap_info *own_bi = NULL;
struct dpp_authentication *auth;
- if (!cmd)
- cmd = "";
+ hostapd_dpp_pkex_clear_code(hapd);
+
+ os_snprintf(cmd, sizeof(cmd), " peer=%u %s", peer_bi->id,
+ hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
wpa_printf(MSG_DEBUG, "DPP: Start authentication after PKEX (cmd: %s)",
cmd);
@@ -342,6 +362,9 @@
return dpp_tcp_auth(hapd->iface->interfaces->dpp, conn, auth,
hapd->conf->dpp_name, DPP_NETROLE_AP,
+ hapd->conf->dpp_mud_url,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value,
hostapd_dpp_process_conf_obj, NULL);
}
#endif /* CONFIG_DPP2 */
@@ -362,7 +385,7 @@
hapd->dpp_pkex = NULL;
pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
hapd->dpp_pkex_identifier,
- hapd->dpp_pkex_code, v2);
+ hapd->dpp_pkex_code, hapd->dpp_pkex_code_len, v2);
if (!pkex)
return -1;
pkex->forced_ver = ver != PKEX_VER_AUTO;
@@ -927,7 +950,10 @@
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,
+ DPP_NETROLE_AP, hapd->conf->dpp_mud_url,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value,
+ hapd->msg_ctx, hapd,
hostapd_dpp_process_conf_obj, NULL);
#endif /* CONFIG_DPP2 */
@@ -975,6 +1001,27 @@
}
+#ifdef CONFIG_DPP2
+static void
+hostapd_dpp_relay_needs_controller(struct hostapd_data *hapd, const u8 *src,
+ enum dpp_public_action_frame_type type)
+{
+ struct os_reltime now;
+
+ if (!hapd->conf->dpp_relay_port)
+ return;
+
+ os_get_reltime(&now);
+ if (hapd->dpp_relay_last_needs_ctrl.sec &&
+ !os_reltime_expired(&now, &hapd->dpp_relay_last_needs_ctrl, 60))
+ return;
+ hapd->dpp_relay_last_needs_ctrl = now;
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_RELAY_NEEDS_CONTROLLER
+ MACSTR " %u", MAC2STR(src), type);
+}
+#endif /* CONFIG_DPP2 */
+
+
static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
@@ -1023,6 +1070,8 @@
src, hdr, buf, len, freq, i_bootstrap,
r_bootstrap, hapd) == 0)
return;
+ hostapd_dpp_relay_needs_controller(hapd, src,
+ DPP_PA_AUTHENTICATION_REQ);
}
#endif /* CONFIG_DPP2 */
if (!own_bi) {
@@ -1031,6 +1080,21 @@
return;
}
+ if (own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ if (!peer_bi || peer_bi->type != DPP_BOOTSTRAP_PKEX) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "No matching peer bootstrapping key found for PKEX - ignore message");
+ return;
+ }
+
+ if (os_memcmp(peer_bi->pubkey_hash, own_bi->peer_pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching peer PKEX bootstrapping key - ignore message");
+ return;
+ }
+ }
+
if (hapd->dpp_auth) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"Already in DPP authentication exchange - ignore new one");
@@ -1286,7 +1350,9 @@
buf = dpp_build_conf_req_helper(auth, hapd->conf->dpp_name,
DPP_NETROLE_AP,
- hapd->conf->dpp_mud_url, NULL);
+ hapd->conf->dpp_mud_url, NULL,
+ hapd->conf->dpp_extra_conf_req_name,
+ hapd->conf->dpp_extra_conf_req_value);
if (!buf) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
@@ -1450,11 +1516,53 @@
}
+#ifdef CONFIG_DPP3
+
+static bool hostapd_dpp_pb_active(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ return ifaces && (ifaces->dpp_pb_time.sec ||
+ ifaces->dpp_pb_time.usec);
+}
+
+
+static void hostapd_dpp_remove_pb_hash(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ int i;
+
+ if (!ifaces->dpp_pb_bi)
+ return;
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ struct dpp_pb_info *info = &ifaces->dpp_pb[i];
+
+ if (info->rx_time.sec == 0 && info->rx_time.usec == 0)
+ continue;
+ if (os_memcmp(info->hash, ifaces->dpp_pb_resp_hash,
+ SHA256_MAC_LEN) == 0) {
+ /* Allow a new push button session to be established
+ * immediately without the successfully completed
+ * session triggering session overlap. */
+ info->rx_time.sec = 0;
+ info->rx_time.usec = 0;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Removed PB hash from session overlap detection due to successfully completed provisioning");
+ }
+ }
+}
+
+#endif /* CONFIG_DPP3 */
+
+
static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
const u8 *hdr, const u8 *buf, size_t len)
{
struct dpp_authentication *auth = hapd->dpp_auth;
enum dpp_status_error status;
+#ifdef CONFIG_DPP3
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
MAC2STR(src));
@@ -1475,7 +1583,8 @@
if (status == DPP_STATUS_OK && auth->send_conn_status) {
wpa_msg(hapd->msg_ctx, MSG_INFO,
- DPP_EVENT_CONF_SENT "wait_conn_status=1");
+ DPP_EVENT_CONF_SENT "wait_conn_status=1 conf_status=%d",
+ auth->conf_resp_status);
wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
hapd, NULL);
@@ -1491,13 +1600,28 @@
hostapd_drv_send_action_cancel_wait(hapd);
hostapd_dpp_listen_stop(hapd);
if (status == DPP_STATUS_OK)
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "conf_status=%d", auth->conf_resp_status);
else
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
dpp_auth_deinit(auth);
hapd->dpp_auth = NULL;
eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
NULL);
+#ifdef CONFIG_DPP3
+ if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+ if (status == DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "success");
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "no-configuration-available");
+ ifaces->dpp_pb_result_indicated = true;
+ if (status == DPP_STATUS_OK)
+ hostapd_dpp_remove_pb_hash(hapd);
+ hostapd_dpp_push_button_stop(hapd);
+ }
+#endif /* CONFIG_DPP3 */
}
@@ -1569,6 +1693,8 @@
return;
wpa_printf(MSG_DEBUG,
"DPP: No matching bootstrapping information found");
+ hostapd_dpp_relay_needs_controller(
+ hapd, src, DPP_PA_PRESENCE_ANNOUNCEMENT);
return;
}
@@ -1659,6 +1785,8 @@
return;
wpa_printf(MSG_DEBUG,
"DPP: No matching Configurator information found");
+ hostapd_dpp_relay_needs_controller(
+ hapd, src, DPP_PA_RECONFIG_ANNOUNCEMENT);
return;
}
@@ -1893,6 +2021,25 @@
}
+static bool hapd_dpp_connector_available(struct hostapd_data *hapd)
+{
+ if (!hapd->wpa_auth ||
+ !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
+ !(hapd->conf->wpa & WPA_PROTO_RSN)) {
+ wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+ return false;
+ }
+
+ if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
+ !hapd->conf->dpp_csign) {
+ wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
+ return false;
+ }
+
+ return true;
+}
+
+
static void hostapd_dpp_rx_peer_disc_req(struct hostapd_data *hapd,
const u8 *src,
const u8 *buf, size_t len,
@@ -1906,20 +2053,12 @@
int expiration;
enum dpp_status_error res;
+ os_memset(&intro, 0, sizeof(intro));
+
wpa_printf(MSG_DEBUG, "DPP: Peer Discovery Request from " MACSTR,
MAC2STR(src));
- if (!hapd->wpa_auth ||
- !(hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) ||
- !(hapd->conf->wpa & WPA_PROTO_RSN)) {
- wpa_printf(MSG_DEBUG, "DPP: DPP AKM not in use");
+ if (!hapd_dpp_connector_available(hapd))
return;
- }
-
- if (!hapd->conf->dpp_connector || !hapd->conf->dpp_netaccesskey ||
- !hapd->conf->dpp_csign) {
- wpa_printf(MSG_DEBUG, "DPP: No own Connector/keys set");
- return;
- }
os_get_time(&now);
@@ -1954,7 +2093,7 @@
wpa_printf(MSG_INFO,
"DPP: Network Introduction protocol resulted in internal failure (peer "
MACSTR ")", MAC2STR(src));
- return;
+ goto done;
}
if (res != DPP_STATUS_OK) {
wpa_printf(MSG_INFO,
@@ -1962,7 +2101,7 @@
MACSTR " status %d)", MAC2STR(src), res);
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
res);
- return;
+ goto done;
}
#ifdef CONFIG_DPP3
@@ -1982,7 +2121,7 @@
hostapd_dpp_send_peer_disc_resp(hapd, src, freq,
trans_id[0],
DPP_STATUS_NO_MATCH);
- return;
+ goto done;
}
}
#endif /* CONFIG_DPP3 */
@@ -1998,11 +2137,13 @@
intro.pmkid, expiration,
WPA_KEY_MGMT_DPP) < 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
- return;
+ goto done;
}
hostapd_dpp_send_peer_disc_resp(hapd, src, freq, trans_id[0],
DPP_STATUS_OK);
+done:
+ dpp_peer_intro_deinit(&intro);
}
@@ -2036,6 +2177,15 @@
goto try_relay;
}
+#ifdef CONFIG_DPP2
+ if (dpp_controller_is_own_pkex_req(hapd->iface->interfaces->dpp,
+ buf, len)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Exchange Request is from local Controller - ignore request");
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+
if (hapd->dpp_pkex) {
/* TODO: Support parallel operations */
wpa_printf(MSG_DEBUG,
@@ -2048,6 +2198,7 @@
hapd->own_addr, src,
hapd->dpp_pkex_identifier,
hapd->dpp_pkex_code,
+ hapd->dpp_pkex_code_len,
buf, len, v2);
if (!hapd->dpp_pkex) {
wpa_printf(MSG_DEBUG,
@@ -2076,9 +2227,12 @@
#ifdef CONFIG_DPP2
if (v2 && dpp_relay_rx_action(hapd->iface->interfaces->dpp,
src, hdr, buf, len, freq, NULL, NULL,
- hapd) != 0)
+ hapd) != 0) {
wpa_printf(MSG_DEBUG,
"DPP: No Relay available for the message");
+ hostapd_dpp_relay_needs_controller(hapd, src,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ }
#else /* CONFIG_DPP2 */
wpa_printf(MSG_DEBUG, "DPP: No relay functionality included - skip");
#endif /* CONFIG_DPP2 */
@@ -2165,6 +2319,7 @@
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
+ hostapd_dpp_pkex_clear_code(hapd);
bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
if (!bi)
return;
@@ -2177,6 +2332,7 @@
const u8 *hdr, const u8 *buf, size_t len,
unsigned int freq)
{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
int res;
struct dpp_bootstrap_info *bi;
struct dpp_pkex *pkex = hapd->dpp_pkex;
@@ -2196,11 +2352,34 @@
return;
}
- bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
+ hostapd_dpp_pkex_clear_code(hapd);
+ bi = dpp_pkex_finish(ifaces->dpp, pkex, src, freq);
if (!bi)
return;
hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+ if (ifaces->dpp_pb_bi &&
+ os_memcmp(bi->pubkey_hash_chirp, ifaces->dpp_pb_resp_hash,
+ SHA256_MAC_LEN) != 0) {
+ char id[20];
+
+ wpa_printf(MSG_INFO,
+ "DPP: Peer bootstrap key from PKEX does not match PB announcement hash");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Peer provided bootstrap key hash(chirp) from PB PKEX",
+ bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Peer provided bootstrap key hash(chirp) from PB announcement",
+ ifaces->dpp_pb_resp_hash, SHA256_MAC_LEN);
+
+ os_snprintf(id, sizeof(id), "%u", bi->id);
+ dpp_bootstrap_remove(ifaces->dpp, id);
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+#endif /* CONFIG_DPP3 */
+
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
hapd->dpp_pkex_auth_cmd ? hapd->dpp_pkex_auth_cmd : "");
@@ -2215,6 +2394,534 @@
}
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_pb_pkex_init(struct hostapd_data *hapd,
+ unsigned int freq, const u8 *src,
+ const u8 *r_hash)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ struct dpp_pkex *pkex;
+ struct wpabuf *msg;
+ char ssid_hex[2 * SSID_MAX_LEN + 1], *pass_hex = NULL;
+ char cmd[300];
+ const char *password = NULL;
+ struct sae_password_entry *e;
+ int conf_id = -1;
+ bool sae = false, psk = false;
+ size_t len;
+
+ if (hapd->dpp_pkex) {
+ wpa_printf(MSG_DEBUG,
+ "PDP: Sending previously generated PKEX Exchange Request to "
+ MACSTR, MAC2STR(src));
+ msg = hapd->dpp_pkex->exchange_req;
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Initiate PKEX for push button with "
+ MACSTR, MAC2STR(src));
+
+ hapd->dpp_pkex_bi = ifaces->dpp_pb_bi;
+ os_memcpy(ifaces->dpp_pb_resp_hash, r_hash, SHA256_MAC_LEN);
+
+ pkex = dpp_pkex_init(hapd->msg_ctx, hapd->dpp_pkex_bi, hapd->own_addr,
+ "PBPKEX", (const char *) ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len,
+ true);
+ if (!pkex) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ pkex->freq = freq;
+
+ hapd->dpp_pkex = pkex;
+ msg = hapd->dpp_pkex->exchange_req;
+
+ if (ifaces->dpp_pb_cmd) {
+ /* Use the externally provided configuration */
+ os_free(hapd->dpp_pkex_auth_cmd);
+ len = 30 + os_strlen(ifaces->dpp_pb_cmd);
+ hapd->dpp_pkex_auth_cmd = os_malloc(len);
+ if (!hapd->dpp_pkex_auth_cmd) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+ hapd->dpp_pkex_bi->id, ifaces->dpp_pb_cmd);
+ goto send_frame;
+ }
+
+ /* Build config based on the current AP configuration */
+ wpa_snprintf_hex(ssid_hex, sizeof(ssid_hex),
+ (const u8 *) hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len);
+
+ if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) {
+ /* TODO: If a local Configurator has been enabled, allow a
+ * DPP AKM credential to be provisioned by setting conf_id. */
+ }
+
+ if (hapd->conf->wpa & WPA_PROTO_RSN) {
+ psk = hapd->conf->wpa_key_mgmt & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256);
+#ifdef CONFIG_SAE
+ sae = hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE;
+#endif /* CONFIG_SAE */
+ }
+
+#ifdef CONFIG_SAE
+ for (e = hapd->conf->sae_passwords; sae && e && !password;
+ e = e->next) {
+ if (e->identifier || !is_broadcast_ether_addr(e->peer_addr))
+ continue;
+ password = e->password;
+ }
+#endif /* CONFIG_SAE */
+ if (!password && hapd->conf->ssid.wpa_passphrase_set &&
+ hapd->conf->ssid.wpa_passphrase)
+ password = hapd->conf->ssid.wpa_passphrase;
+ if (password) {
+ len = 2 * os_strlen(password) + 1;
+ pass_hex = os_malloc(len);
+ if (!pass_hex) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ wpa_snprintf_hex(pass_hex, len, (const u8 *) password,
+ os_strlen(password));
+ }
+
+ if (conf_id > 0 && sae && psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp+psk+sae configurator=%d ssid=%s pass=%s",
+ conf_id, ssid_hex, pass_hex);
+ } else if (conf_id > 0 && sae && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp+sae configurator=%d ssid=%s pass=%s",
+ conf_id, ssid_hex, pass_hex);
+ } else if (conf_id > 0) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-dpp configurator=%d ssid=%s",
+ conf_id, ssid_hex);
+ } if (sae && psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-psk+sae ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else if (sae && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-sae ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else if (psk && pass_hex) {
+ os_snprintf(cmd, sizeof(cmd),
+ "conf=sta-psk ssid=%s pass=%s",
+ ssid_hex, pass_hex);
+ } else {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported AP configuration for push button");
+ str_clear_free(pass_hex);
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ str_clear_free(pass_hex);
+
+ os_free(hapd->dpp_pkex_auth_cmd);
+ len = 30 + os_strlen(cmd);
+ hapd->dpp_pkex_auth_cmd = os_malloc(len);
+ if (hapd->dpp_pkex_auth_cmd)
+ os_snprintf(hapd->dpp_pkex_auth_cmd, len, " own=%d %s",
+ hapd->dpp_pkex_bi->id, cmd);
+ forced_memzero(cmd, sizeof(cmd));
+ if (!hapd->dpp_pkex_auth_cmd) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+send_frame:
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PKEX_EXCHANGE_REQ);
+ hostapd_drv_send_action(hapd, pkex->freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ pkex->exch_req_wait_time = 2000;
+ pkex->exch_req_tries = 1;
+}
+
+
+static void
+hostapd_dpp_rx_pb_presence_announcement(struct hostapd_data *hapd,
+ const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+ const u8 *r_hash;
+ u16 r_hash_len;
+ unsigned int i;
+ bool found = false;
+ struct dpp_pb_info *info, *tmp;
+ struct os_reltime now, age;
+ struct wpabuf *msg;
+
+ if (!ifaces)
+ return;
+
+ os_get_reltime(&now);
+ wpa_printf(MSG_DEBUG, "DPP: Push Button Presence Announcement from "
+ MACSTR, MAC2STR(src));
+
+ r_hash = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_hash_len);
+ if (!r_hash || r_hash_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_hash, r_hash_len);
+
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ info = &ifaces->dpp_pb[i];
+ if ((info->rx_time.sec == 0 && info->rx_time.usec == 0) ||
+ os_memcmp(r_hash, info->hash, SHA256_MAC_LEN) != 0)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Active push button Enrollee already known");
+ found = true;
+ info->rx_time = now;
+ }
+
+ if (!found) {
+ for (i = 0; i < DPP_PB_INFO_COUNT; i++) {
+ tmp = &ifaces->dpp_pb[i];
+ if (tmp->rx_time.sec == 0 && tmp->rx_time.usec == 0)
+ continue;
+
+ if (os_reltime_expired(&now, &tmp->rx_time, 120)) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Push button Enrollee hash expired",
+ tmp->hash, SHA256_MAC_LEN);
+ tmp->rx_time.sec = 0;
+ tmp->rx_time.usec = 0;
+ continue;
+ }
+
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Push button session overlap with hash",
+ tmp->hash, SHA256_MAC_LEN);
+ if (!ifaces->dpp_pb_result_indicated &&
+ hostapd_dpp_pb_active(hapd)) {
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_PB_RESULT "session-overlap");
+ ifaces->dpp_pb_result_indicated = true;
+ }
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+ /* Replace the oldest entry */
+ info = &ifaces->dpp_pb[0];
+ for (i = 1; i < DPP_PB_INFO_COUNT; i++) {
+ tmp = &ifaces->dpp_pb[i];
+ if (os_reltime_before(&tmp->rx_time, &info->rx_time))
+ info = tmp;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: New active push button Enrollee");
+ os_memcpy(info->hash, r_hash, SHA256_MAC_LEN);
+ info->rx_time = now;
+ }
+
+ if (!hostapd_dpp_pb_active(hapd)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Discard message since own push button has not been pressed");
+ return;
+ }
+
+ if (ifaces->dpp_pb_announce_time.sec == 0 &&
+ ifaces->dpp_pb_announce_time.usec == 0) {
+ /* Start a wait before allowing PKEX to be initiated */
+ ifaces->dpp_pb_announce_time = now;
+ }
+
+ if (!ifaces->dpp_pb_bi) {
+ int res;
+
+ res = dpp_bootstrap_gen(ifaces->dpp, "type=pkex");
+ if (res < 0)
+ return;
+ ifaces->dpp_pb_bi = dpp_bootstrap_get_id(ifaces->dpp, res);
+ if (!ifaces->dpp_pb_bi)
+ return;
+
+ if (random_get_bytes(ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate C-nonce");
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+ }
+
+ /* Skip the response if one was sent within last 50 ms since the
+ * Enrollee is going to send out at least three announcement messages.
+ */
+ os_reltime_sub(&now, &ifaces->dpp_pb_last_resp, &age);
+ if (age.sec == 0 && age.usec < 50000) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Skip Push Button Presence Announcement Response frame immediately after having sent one");
+ return;
+ }
+
+ msg = dpp_build_pb_announcement_resp(
+ ifaces->dpp_pb_bi, r_hash, ifaces->dpp_pb_c_nonce,
+ ifaces->dpp_pb_bi->curve->nonce_len);
+ if (!msg) {
+ hostapd_dpp_push_button_stop(hapd);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Send Push Button Presence Announcement Response to "
+ MACSTR, MAC2STR(src));
+ ifaces->dpp_pb_last_resp = now;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ if (os_reltime_expired(&now, &ifaces->dpp_pb_announce_time, 15))
+ hostapd_dpp_pb_pkex_init(hapd, freq, src, r_hash);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_query(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ const u8 *trans_id, *version;
+ u16 trans_id_len, version_len;
+ struct wpabuf *msg;
+ u8 ver = DPP_VERSION;
+ int conn_ver;
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Query from "
+ MACSTR, MAC2STR(src));
+
+ if (!hapd_dpp_connector_available(hapd))
+ return;
+
+ trans_id = dpp_get_attr(buf, 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");
+ return;
+ }
+
+ version = dpp_get_attr(buf, len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Protocol Version");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Transaction ID %u, Version %u",
+ trans_id[0], version[0]);
+
+ len = 5 + 5 + 4 + os_strlen(hapd->conf->dpp_connector);
+ msg = dpp_alloc_msg(DPP_PA_PRIV_PEER_INTRO_NOTIFY, len);
+ if (!msg)
+ return;
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, trans_id[0]);
+
+ /* Protocol Version */
+ conn_ver = dpp_get_connector_version(hapd->conf->dpp_connector);
+ if (conn_ver > 0 && ver != conn_ver) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Use Connector version %d instead of current protocol version %d",
+ conn_ver, ver);
+ ver = conn_ver;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, ver);
+
+ /* DPP Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(hapd->conf->dpp_connector));
+ wpabuf_put_str(msg, hapd->conf->dpp_connector);
+
+ wpa_printf(MSG_DEBUG, "DPP: Send Private Peer Introduction Notify to "
+ MACSTR, MAC2STR(src));
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_TX "dst=" MACSTR
+ " freq=%u type=%d", MAC2STR(src), freq,
+ DPP_PA_PRIV_PEER_INTRO_NOTIFY);
+ hostapd_drv_send_action(hapd, freq, 0, src,
+ wpabuf_head(msg), wpabuf_len(msg));
+ wpabuf_free(msg);
+}
+
+
+static void
+hostapd_dpp_rx_priv_peer_intro_update(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len,
+ unsigned int freq)
+{
+ struct crypto_ec_key *own_key;
+ const struct dpp_curve_params *curve;
+ enum hpke_kem_id kem_id;
+ enum hpke_kdf_id kdf_id;
+ enum hpke_aead_id aead_id;
+ const u8 *aad = hdr;
+ size_t aad_len = DPP_HDR_LEN;
+ struct wpabuf *pt;
+ const u8 *trans_id, *wrapped, *version, *connector;
+ u16 trans_id_len, wrapped_len, version_len, connector_len;
+ struct os_time now;
+ struct dpp_introduction intro;
+ os_time_t expire;
+ int expiration;
+ enum dpp_status_error res;
+
+ os_memset(&intro, 0, sizeof(intro));
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction Update from "
+ MACSTR, MAC2STR(src));
+
+ if (!hapd_dpp_connector_available(hapd))
+ return;
+
+ os_get_time(&now);
+
+ if (hapd->conf->dpp_netaccesskey_expiry &&
+ (os_time_t) hapd->conf->dpp_netaccesskey_expiry < now.sec) {
+ wpa_printf(MSG_INFO, "DPP: Own netAccessKey expired");
+ return;
+ }
+
+ trans_id = dpp_get_attr(buf, 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");
+ return;
+ }
+
+ wrapped = dpp_get_attr(buf, len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_len);
+ if (!wrapped) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer did not include Wrapped Data");
+ return;
+ }
+
+ own_key = dpp_set_keypair(&curve,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ return;
+ }
+
+ if (dpp_hpke_suite(curve->ike_group, &kem_id, &kdf_id, &aead_id) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Unsupported curve %d",
+ curve->ike_group);
+ crypto_ec_key_deinit(own_key);
+ return;
+ }
+
+ pt = hpke_base_open(kem_id, kdf_id, aead_id, own_key, NULL, 0,
+ aad, aad_len, wrapped, wrapped_len);
+ crypto_ec_key_deinit(own_key);
+ if (!pt) {
+ wpa_printf(MSG_INFO, "DPP: Failed to decrypt Connector");
+ return;
+ }
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: HPKE-Decrypted Wrapped Data", pt);
+
+ connector = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+ DPP_ATTR_CONNECTOR, &connector_len);
+ if (!connector) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include its Connector");
+ goto done;
+ }
+
+ version = dpp_get_attr(wpabuf_head(pt), wpabuf_len(pt),
+ DPP_ATTR_PROTOCOL_VERSION, &version_len);
+ if (!version || version_len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Protocol Version");
+ goto done;
+ }
+
+ res = dpp_peer_intro(&intro, hapd->conf->dpp_connector,
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey),
+ wpabuf_head(hapd->conf->dpp_csign),
+ wpabuf_len(hapd->conf->dpp_csign),
+ connector, connector_len, &expire);
+ if (res == 255) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in internal failure (peer "
+ MACSTR ")", MAC2STR(src));
+ goto done;
+ }
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_INFO,
+ "DPP: Network Introduction protocol resulted in failure (peer "
+ MACSTR " status %d)", MAC2STR(src), res);
+ goto done;
+ }
+
+ if (intro.peer_version && intro.peer_version >= 2) {
+ u8 attr_version = 1;
+
+ if (version && version_len >= 1)
+ attr_version = version[0];
+ if (attr_version != intro.peer_version) {
+ wpa_printf(MSG_INFO,
+ "DPP: Protocol version mismatch (Connector: %d Attribute: %d",
+ intro.peer_version, attr_version);
+ goto done;
+ }
+ }
+
+ if (!expire || (os_time_t) hapd->conf->dpp_netaccesskey_expiry < expire)
+ expire = hapd->conf->dpp_netaccesskey_expiry;
+ if (expire)
+ expiration = expire - now.sec;
+ else
+ expiration = 0;
+
+ if (wpa_auth_pmksa_add2(hapd->wpa_auth, src, intro.pmk, intro.pmk_len,
+ intro.pmkid, expiration,
+ WPA_KEY_MGMT_DPP) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to add PMKSA cache entry");
+ goto done;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Private Peer Introduction completed with "
+ MACSTR, MAC2STR(src));
+
+done:
+ dpp_peer_intro_deinit(&intro);
+ wpabuf_free(pt);
+}
+
+#endif /* CONFIG_DPP3 */
+
+
void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
const u8 *buf, size_t len, unsigned int freq)
{
@@ -2320,6 +3027,20 @@
freq);
break;
#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_DPP3
+ case DPP_PA_PB_PRESENCE_ANNOUNCEMENT:
+ hostapd_dpp_rx_pb_presence_announcement(hapd, src, hdr,
+ buf, len, freq);
+ break;
+ case DPP_PA_PRIV_PEER_INTRO_QUERY:
+ hostapd_dpp_rx_priv_peer_intro_query(hapd, src, hdr,
+ buf, len, freq);
+ break;
+ case DPP_PA_PRIV_PEER_INTRO_UPDATE:
+ hostapd_dpp_rx_priv_peer_intro_update(hapd, src, hdr,
+ buf, len, freq);
+ break;
+#endif /* CONFIG_DPP3 */
default:
wpa_printf(MSG_DEBUG,
"DPP: Ignored unsupported frame subtype %d", type);
@@ -2388,6 +3109,9 @@
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
{
struct dpp_authentication *auth = hapd->dpp_auth;
+#ifdef CONFIG_DPP3
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+#endif /* CONFIG_DPP3 */
if (!auth)
return;
@@ -2422,11 +3146,26 @@
hostapd_drv_send_action_cancel_wait(hapd);
if (ok)
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "conf_status=%d", auth->conf_resp_status);
else
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
+#ifdef CONFIG_DPP3
+ if (!ifaces->dpp_pb_result_indicated && hostapd_dpp_pb_active(hapd)) {
+ if (ok)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "success");
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_PB_RESULT
+ "could-not-connect");
+ ifaces->dpp_pb_result_indicated = true;
+ if (ok)
+ hostapd_dpp_remove_pb_hash(hapd);
+ hostapd_dpp_push_button_stop(hapd);
+ }
+#endif /* CONFIG_DPP3 */
}
@@ -2528,6 +3267,7 @@
hapd->dpp_pkex_code = os_strdup(pos + 6);
if (!hapd->dpp_pkex_code)
return -1;
+ hapd->dpp_pkex_code_len = os_strlen(hapd->dpp_pkex_code);
pos = os_strstr(cmd, " ver=");
if (pos) {
@@ -2576,7 +3316,7 @@
return -1;
}
- if ((id_val != 0 && id_val != 1) || !hapd->dpp_pkex_code)
+ if ((id_val != 0 && id_val != 1))
return -1;
/* TODO: Support multiple PKEX entries */
@@ -2600,6 +3340,9 @@
hapd->dpp_auth = NULL;
dpp_pkex_free(hapd->dpp_pkex);
hapd->dpp_pkex = NULL;
+#ifdef CONFIG_DPP3
+ hostapd_dpp_push_button_stop(hapd);
+#endif /* CONFIG_DPP3 */
}
@@ -2611,6 +3354,9 @@
struct hostapd_data *hapd = ctx;
u8 *buf;
+ if (freq == 0)
+ freq = hapd->iface->freq;
+
wpa_printf(MSG_DEBUG, "DPP: Send action frame dst=" MACSTR " freq=%u",
MAC2STR(addr), freq);
buf = os_malloc(2 + len);
@@ -2643,6 +3389,7 @@
struct dpp_relay_config config;
os_memset(&config, 0, sizeof(config));
+ config.msg_ctx = hapd->msg_ctx;
config.cb_ctx = hapd;
config.tx = hostapd_dpp_relay_tx;
config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
@@ -2653,12 +3400,76 @@
&config) < 0)
return -1;
}
+
+ if (hapd->conf->dpp_relay_port)
+ dpp_relay_listen(hapd->iface->interfaces->dpp,
+ hapd->conf->dpp_relay_port,
+ &config);
#endif /* CONFIG_DPP2 */
return 0;
}
+#ifdef CONFIG_DPP2
+
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd)
+{
+ struct dpp_relay_config config;
+ struct hostapd_ip_addr addr;
+ u8 pkhash[SHA256_MAC_LEN];
+ char *pos, *tmp;
+ int ret = -1;
+ bool prev_state, new_state;
+ struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+ tmp = os_strdup(cmd);
+ if (!tmp)
+ goto fail;
+ pos = os_strchr(tmp, ' ');
+ if (!pos)
+ goto fail;
+ *pos++ = '\0';
+ if (hostapd_parse_ip_addr(tmp, &addr) < 0 ||
+ hexstr2bin(pos, pkhash, SHA256_MAC_LEN) < 0)
+ goto fail;
+
+ os_memset(&config, 0, sizeof(config));
+ config.msg_ctx = hapd->msg_ctx;
+ config.cb_ctx = hapd;
+ config.tx = hostapd_dpp_relay_tx;
+ config.gas_resp_tx = hostapd_dpp_relay_gas_resp_tx;
+ config.ipaddr = &addr;
+ config.pkhash = pkhash;
+ prev_state = dpp_relay_controller_available(dpp);
+ ret = dpp_relay_add_controller(dpp, &config);
+ new_state = dpp_relay_controller_available(dpp);
+ if (new_state != prev_state)
+ ieee802_11_update_beacons(hapd->iface);
+fail:
+ os_free(tmp);
+ return ret;
+}
+
+
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd)
+{
+ struct hostapd_ip_addr addr;
+ bool prev_state, new_state;
+ struct dpp_global *dpp = hapd->iface->interfaces->dpp;
+
+ if (hostapd_parse_ip_addr(cmd, &addr) < 0)
+ return;
+ prev_state = dpp_relay_controller_available(dpp);
+ dpp_relay_remove_controller(dpp, &addr);
+ new_state = dpp_relay_controller_available(dpp);
+ if (new_state != prev_state)
+ ieee802_11_update_beacons(hapd->iface);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
int hostapd_dpp_init(struct hostapd_data *hapd)
{
hapd->dpp_allowed_roles = DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE;
@@ -2693,11 +3504,14 @@
eloop_cancel_timeout(hostapd_dpp_conn_status_result_wait_timeout, hapd,
NULL);
hostapd_dpp_chirp_stop(hapd);
- if (hapd->iface->interfaces)
+ if (hapd->iface->interfaces) {
+ dpp_relay_stop_listen(hapd->iface->interfaces->dpp);
dpp_controller_stop_for_ctx(hapd->iface->interfaces->dpp, hapd);
+ }
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_DPP3
eloop_cancel_timeout(hostapd_dpp_build_new_key, hapd, NULL);
+ hostapd_dpp_push_button_stop(hapd);
#endif /* CONFIG_DPP3 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
@@ -2705,6 +3519,8 @@
hapd->dpp_pkex = NULL;
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
}
@@ -3077,3 +3893,84 @@
}
#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+static void hostapd_dpp_push_button_expire(void *eloop_ctx, void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: Active push button mode expired");
+ hostapd_dpp_push_button_stop(hapd);
+}
+
+
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ if (!ifaces || !ifaces->dpp)
+ return -1;
+ os_get_reltime(&ifaces->dpp_pb_time);
+ ifaces->dpp_pb_announce_time.sec = 0;
+ ifaces->dpp_pb_announce_time.usec = 0;
+ str_clear_free(ifaces->dpp_pb_cmd);
+ ifaces->dpp_pb_cmd = NULL;
+ if (cmd) {
+ ifaces->dpp_pb_cmd = os_strdup(cmd);
+ if (!ifaces->dpp_pb_cmd)
+ return -1;
+ }
+ eloop_register_timeout(100, 0, hostapd_dpp_push_button_expire,
+ hapd, NULL);
+
+ return 0;
+}
+
+
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd)
+{
+ struct hapd_interfaces *ifaces = hapd->iface->interfaces;
+
+ if (!ifaces || !ifaces->dpp)
+ return;
+ eloop_cancel_timeout(hostapd_dpp_push_button_expire, hapd, NULL);
+ if (hostapd_dpp_pb_active(hapd)) {
+ wpa_printf(MSG_DEBUG, "DPP: Stop active push button mode");
+ if (!ifaces->dpp_pb_result_indicated)
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_PB_RESULT "failed");
+ }
+ ifaces->dpp_pb_time.sec = 0;
+ ifaces->dpp_pb_time.usec = 0;
+ dpp_pkex_free(hapd->dpp_pkex);
+ hapd->dpp_pkex = NULL;
+ os_free(hapd->dpp_pkex_auth_cmd);
+ hapd->dpp_pkex_auth_cmd = NULL;
+
+ if (ifaces->dpp_pb_bi) {
+ char id[20];
+
+ os_snprintf(id, sizeof(id), "%u", ifaces->dpp_pb_bi->id);
+ dpp_bootstrap_remove(ifaces->dpp, id);
+ ifaces->dpp_pb_bi = NULL;
+ }
+
+ ifaces->dpp_pb_result_indicated = false;
+
+ str_clear_free(ifaces->dpp_pb_cmd);
+ ifaces->dpp_pb_cmd = NULL;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+#ifdef CONFIG_DPP2
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd)
+{
+ return hapd->conf->dpp_configurator_connectivity ||
+ (hapd->iface->interfaces &&
+ dpp_relay_controller_available(hapd->iface->interfaces->dpp));
+}
+#endif /* CONFIG_DPP2 */
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 264d3e4..55f1fce 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -45,5 +45,10 @@
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);
+int hostapd_dpp_push_button(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_push_button_stop(struct hostapd_data *hapd);
+bool hostapd_dpp_configurator_connectivity(struct hostapd_data *hapd);
+int hostapd_dpp_add_controller(struct hostapd_data *hapd, const char *cmd);
+void hostapd_dpp_remove_controller(struct hostapd_data *hapd, const char *cmd);
#endif /* DPP_HOSTAPD_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 05faa0f..3681df6 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1238,7 +1238,8 @@
* Short SSID calculation is identical to FCS and it is defined in
* IEEE P802.11-REVmd/D3.0, 9.4.2.170.3 (Calculating the Short-SSID).
*/
- conf->ssid.short_ssid = crc32(conf->ssid.ssid, conf->ssid.ssid_len);
+ conf->ssid.short_ssid = ieee80211_crc32(conf->ssid.ssid,
+ conf->ssid.ssid_len);
if (!hostapd_drv_none(hapd)) {
wpa_printf(MSG_DEBUG, "Using interface %s with hwaddr " MACSTR
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 15d59dc..e121362 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -76,6 +76,17 @@
#ifdef CONFIG_DPP
struct dpp_global *dpp;
+#ifdef CONFIG_DPP3
+ struct os_reltime dpp_pb_time;
+ struct os_reltime dpp_pb_announce_time;
+ struct dpp_pb_info dpp_pb[DPP_PB_INFO_COUNT];
+ struct dpp_bootstrap_info *dpp_pb_bi;
+ u8 dpp_pb_c_nonce[DPP_MAX_NONCE_LEN];
+ u8 dpp_pb_resp_hash[SHA256_MAC_LEN];
+ struct os_reltime dpp_pb_last_resp;
+ bool dpp_pb_result_indicated;
+ char *dpp_pb_cmd;
+#endif /* CONFIG_DPP3 */
#endif /* CONFIG_DPP */
#ifdef CONFIG_CTRL_IFACE_UDP
@@ -401,6 +412,7 @@
struct dpp_pkex *dpp_pkex;
struct dpp_bootstrap_info *dpp_pkex_bi;
char *dpp_pkex_code;
+ size_t dpp_pkex_code_len;
char *dpp_pkex_identifier;
enum dpp_pkex_ver dpp_pkex_ver;
char *dpp_pkex_auth_cmd;
@@ -421,6 +433,7 @@
int dpp_chirp_round;
int dpp_chirp_scan_done;
int dpp_chirp_listen;
+ struct os_reltime dpp_relay_last_needs_ctrl;
#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
char *dpp_config_obj_override;
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 8806a58..a65a296 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -982,7 +982,8 @@
sta->sae->peer_commit_scalar_accepted = sta->sae->peer_commit_scalar;
sta->sae->peer_commit_scalar = NULL;
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
- sta->sae->pmk, sta->sae->pmkid);
+ sta->sae->pmk, sta->sae->pmk_len,
+ sta->sae->pmkid, sta->sae->akmp);
sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
}
@@ -1233,6 +1234,10 @@
if (sae_pwe == 0 && sae_pk)
sae_pwe = 2;
#endif /* CONFIG_SAE_PK */
+ if (sae_pwe == 0 &&
+ (hapd->conf->wpa_key_mgmt &
+ (WPA_KEY_MGMT_SAE_EXT_KEY | WPA_KEY_MGMT_FT_SAE_EXT_KEY)))
+ sae_pwe = 2;
return ((sae_pwe == 0 || sae_pwe == 3) &&
status_code == WLAN_STATUS_SUCCESS) ||
@@ -2514,7 +2519,8 @@
* restrict this only for PASN.
*/
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
- pasn->sae.pmk, pasn->sae.pmkid);
+ pasn->sae.pmk, pasn->sae.pmk_len,
+ pasn->sae.pmkid, pasn->sae.akmp);
return 0;
}
@@ -2661,6 +2667,15 @@
goto fail;
}
+ if (pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&pasn->ptk, pasn->akmp, pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Failed to derive LTF keyseed");
+ goto fail;
+ }
+ }
+
wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
wpabuf_free(pasn->secret);
@@ -2842,6 +2857,38 @@
}
+static int pasn_set_keys_from_cache(struct hostapd_data *hapd,
+ const u8 *own_addr, const u8 *sta_addr,
+ int cipher, int akmp)
+{
+ struct ptksa_cache_entry *entry;
+
+ entry = ptksa_cache_get(hapd->ptksa, sta_addr, cipher);
+ if (!entry) {
+ wpa_printf(MSG_DEBUG, "PASN: peer " MACSTR
+ " not present in PTKSA cache", MAC2STR(sta_addr));
+ return -1;
+ }
+
+ if (os_memcmp(entry->own_addr, own_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: own addr " MACSTR " and PTKSA entry own addr "
+ MACSTR " differ",
+ MAC2STR(own_addr), MAC2STR(entry->own_addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: " MACSTR " present in PTKSA cache",
+ MAC2STR(sta_addr));
+ hostapd_drv_set_secure_ranging_ctx(hapd, own_addr, sta_addr, cipher,
+ entry->ptk.tk_len, entry->ptk.tk,
+ entry->ptk.ltf_keyseed_len,
+ entry->ptk.ltf_keyseed, 0);
+
+ return 0;
+}
+
+
static int
pasn_derive_keys(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *cached_pmk, size_t cached_pmk_len,
@@ -2898,6 +2945,16 @@
return -1;
}
+ if (sta->pasn->secure_ltf) {
+ ret = wpa_ltf_keyseed(&sta->pasn->ptk, sta->pasn->akmp,
+ sta->pasn->cipher);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive LTF keyseed");
+ return -1;
+ }
+ }
+
wpa_printf(MSG_DEBUG, "PASN: PTK successfully derived");
return 0;
}
@@ -3161,7 +3218,7 @@
sta->pasn->akmp = rsn_data.key_mgmt;
sta->pasn->cipher = rsn_data.pairwise_cipher;
- derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF) &&
+ derive_kdk = (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
WLAN_RSNX_CAPAB_SECURE_LTF);
#ifdef CONFIG_TESTING_OPTIONS
@@ -3174,6 +3231,13 @@
sta->pasn->kdk_len = 0;
wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", sta->pasn->kdk_len);
+ if ((hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP) &&
+ ieee802_11_rsnx_capab_len(elems.rsnxe, elems.rsnxe_len,
+ WLAN_RSNX_CAPAB_SECURE_LTF))
+ sta->pasn->secure_ltf = true;
+ else
+ sta->pasn->secure_ltf = false;
+
if (!elems.pasn_params || !elems.pasn_params_len) {
wpa_printf(MSG_DEBUG,
"PASN: No PASN Parameters element found");
@@ -3496,8 +3560,10 @@
wpa_printf(MSG_INFO,
"PASN: Success handling transaction == 3. Store PTK");
- ptksa_cache_add(hapd->ptksa, sta->addr, sta->pasn->cipher, 43200,
- &sta->pasn->ptk);
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, sta->addr,
+ sta->pasn->cipher, 43200, &sta->pasn->ptk, NULL, NULL);
+ pasn_set_keys_from_cache(hapd, hapd->own_addr, sta->addr,
+ sta->pasn->cipher, sta->pasn->akmp);
fail:
ap_free_sta(hapd, sta);
}
@@ -4686,7 +4752,7 @@
sta->auth_alg == WLAN_AUTH_OPEN) {
struct rsn_pmksa_cache_entry *sa;
sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
- if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+ if (!sa || !wpa_key_mgmt_sae(sa->akmp)) {
wpa_printf(MSG_DEBUG,
"SAE: No PMKSA cache entry found for "
MACSTR, MAC2STR(sta->addr));
@@ -7450,7 +7516,7 @@
break;
*eid++ = RNR_NEIGHBOR_AP_OFFSET_UNKNOWN;
- os_memcpy(eid, bss->conf->bssid, ETH_ALEN);
+ os_memcpy(eid, bss->own_addr, ETH_ALEN);
eid += ETH_ALEN;
os_memcpy(eid, &bss->conf->ssid.short_ssid, 4);
eid += 4;
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index b5b7e5d..12273c3 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -533,7 +533,8 @@
u8 *mac_cap;
if (!hapd->iface->current_mode ||
- !hapd->iface->current_mode->he_capab[mode].he_supported)
+ !hapd->iface->current_mode->he_capab[mode].he_supported ||
+ !hapd->iconf->ieee80211ax || hapd->conf->disable_11ax)
return 0;
mac_cap = hapd->iface->current_mode->he_capab[mode].mac_cap;
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 4e7c33e..eaeaec5 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -17,6 +17,7 @@
#include "ap_config.h"
#include "ap_drv_ops.h"
#include "wpa_auth.h"
+#include "dpp_hostapd.h"
#include "ieee802_11.h"
@@ -412,8 +413,7 @@
*pos |= 0x01;
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211AX
- if (hapd->iconf->ieee80211ax &&
- hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
+ if (hostapd_get_he_twt_responder(hapd, IEEE80211_MODE_AP))
*pos |= 0x40; /* Bit 78 - TWT responder */
#endif /* CONFIG_IEEE80211AX */
break;
@@ -873,7 +873,7 @@
size_t hostapd_eid_dpp_cc_len(struct hostapd_data *hapd)
{
#ifdef CONFIG_DPP2
- if (hapd->conf->dpp_configurator_connectivity)
+ if (hostapd_dpp_configurator_connectivity(hapd))
return 6;
#endif /* CONFIG_DPP2 */
return 0;
@@ -885,7 +885,7 @@
u8 *pos = eid;
#ifdef CONFIG_DPP2
- if (!hapd->conf->dpp_configurator_connectivity || len < 6)
+ if (!hostapd_dpp_configurator_connectivity(hapd) || len < 6)
return pos;
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
@@ -1063,7 +1063,8 @@
if (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) || sae_pk) &&
+ hostapd_sae_pw_id_in_use(hapd->conf) || sae_pk ||
+ wpa_key_mgmt_sae_ext_key(hapd->conf->wpa_key_mgmt)) &&
hapd->conf->sae_pwe != 3) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
@@ -1072,11 +1073,11 @@
#endif /* CONFIG_SAE_PK */
}
- if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
- if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP)
capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
- if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
+ if (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP)
capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
flen = (capab & 0xff00) ? 2 : 1;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index 52f25eb..5b276e8 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -136,7 +136,7 @@
os_memcpy(entry->bssid, bssid, ETH_ALEN);
os_memcpy(&entry->ssid, ssid, sizeof(entry->ssid));
- entry->short_ssid = crc32(ssid->ssid, ssid->ssid_len);
+ entry->short_ssid = ieee80211_crc32(ssid->ssid, ssid->ssid_len);
entry->nr = wpabuf_dup(nr);
if (!entry->nr)
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 5c92e01..d2a8344 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -88,6 +88,7 @@
int akmp;
int cipher;
u16 group;
+ bool secure_ltf;
u8 trans_seq;
u8 wrapped_data_format;
size_t kdk_len;
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 2954af6..64a1800 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -149,6 +149,20 @@
}
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+ const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ if (!wpa_auth->cb->set_ltf_keyseed)
+ return -1;
+ return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+ ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth,
const u8 *addr, int idx, u8 *seq)
{
@@ -2320,6 +2334,7 @@
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
int akmp;
+ int ret;
if (sm->wpa_auth->conf.force_kdk_derivation ||
(sm->wpa_auth->conf.secure_ltf &&
@@ -2333,16 +2348,33 @@
if (sm->ft_completed) {
u8 ptk_name[WPA_PMK_NAME_LEN];
- return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
- sm->SNonce, sm->ANonce,
- sm->addr, sm->wpa_auth->addr,
- sm->pmk_r1_name,
- ptk, ptk_name,
- sm->wpa_key_mgmt,
- sm->pairwise,
- kdk_len);
+ ret = wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+ sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr,
+ sm->pmk_r1_name, ptk,
+ ptk_name, sm->wpa_key_mgmt,
+ sm->pairwise, kdk_len);
+ } else {
+ ret = wpa_auth_derive_ptk_ft(sm, ptk);
}
- return wpa_auth_derive_ptk_ft(sm, ptk);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt,
+ sm->pairwise);
+ if (ret) {
+ wpa_printf(MSG_ERROR,
+ "FT: LTF keyseed derivation failed");
+ }
+ }
+#endif /* CONFIG_PASN */
+ return ret;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -2356,9 +2388,27 @@
akmp = sm->wpa_key_mgmt;
if (force_sha256)
akmp |= WPA_KEY_MGMT_PSK_SHA256;
- return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
- sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
- ptk, akmp, sm->pairwise, z, z_len, kdk_len);
+ ret = wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
+ sm->wpa_auth->addr, sm->addr, sm->ANonce,
+ snonce, ptk, akmp, sm->pairwise, z, z_len,
+ kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ ret = wpa_ltf_keyseed(ptk, sm->wpa_key_mgmt, sm->pairwise);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: LTF keyseed derivation failed");
+ }
+ }
+#endif /* CONFIG_PASN */
+ return ret;
}
@@ -2389,6 +2439,19 @@
fils_ft, &fils_ft_len, kdk_len);
if (res < 0)
return res;
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)) {
+ res = wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, sm->pairwise);
+ if (res) {
+ wpa_printf(MSG_ERROR,
+ "FILS: LTF keyseed derivation failed");
+ return res;
+ }
+ }
+#endif /* CONFIG_PASN */
+
sm->PTK_valid = true;
sm->tk_already_set = false;
@@ -2892,6 +2955,19 @@
wpa_printf(MSG_DEBUG, "FILS: Failed to set TK to the driver");
return -1;
}
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "FILS: Failed to set LTF keyseed to driver");
+ return -1;
+ }
+#endif /* CONFIG_PASN */
+
sm->pairwise_set = true;
sm->tk_already_set = true;
@@ -3490,6 +3566,21 @@
return;
}
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to set LTF keyseed to driver");
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_PASN */
+
/* WPA2 send GTK in the 4-way handshake */
secure = 1;
gtk = gsm->GTK[gsm->GN - 1];
@@ -3701,6 +3792,22 @@
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
+
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: Failed to set LTF keyseed to driver");
+ wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+#endif /* CONFIG_PASN */
+
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = true;
@@ -4869,16 +4976,17 @@
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, const u8 *pmkid)
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int akmp)
{
if (wpa_auth->conf.disable_pmksa_caching)
return -1;
- wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
- if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
- NULL, 0,
- wpa_auth->addr, addr, 0, NULL,
- WPA_KEY_MGMT_SAE))
+ wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, pmk_len);
+ if (!akmp)
+ akmp = WPA_KEY_MGMT_SAE;
+ if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, pmk_len, pmkid,
+ NULL, 0, wpa_auth->addr, addr, 0, NULL, akmp))
return 0;
return -1;
@@ -4956,13 +5064,14 @@
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ size_t pmk_len, int akmp,
const u8 *pmkid, int expiration)
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
- entry = pmksa_cache_auth_create_entry(pmk, PMK_LEN, pmkid, NULL, 0, aa,
- spa, 0, NULL, WPA_KEY_MGMT_SAE);
+ entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, NULL, 0, aa,
+ spa, 0, NULL, akmp);
if (!entry)
return NULL;
@@ -5276,7 +5385,8 @@
{
if (!sm)
return 0;
- return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+ return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY;
}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index 348a1de..a18f7cb 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -352,6 +352,10 @@
#ifdef CONFIG_MESH
int (*start_ampe)(void *ctx, const u8 *sta_addr);
#endif /* CONFIG_MESH */
+#ifdef CONFIG_PASN
+ int (*set_ltf_keyseed)(void *ctx, const u8 *addr, const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len);
+#endif /* CONFIG_PASN */
};
struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -427,7 +431,8 @@
int session_timeout,
struct eapol_state_machine *eapol);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
- const u8 *pmk, const u8 *pmkid);
+ const u8 *pmk, size_t pmk_len, const u8 *pmkid,
+ int akmp);
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
int wpa_auth_pmksa_add2(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
@@ -441,6 +446,7 @@
char *buf, size_t len);
struct rsn_pmksa_cache_entry *
wpa_auth_pmksa_create_entry(const u8 *aa, const u8 *spa, const u8 *pmk,
+ size_t pmk_len, int akmp,
const u8 *pmkid, int expiration);
int wpa_auth_pmksa_add_entry(struct wpa_authenticator *wpa_auth,
struct rsn_pmksa_cache_entry *entry);
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index 7a97613..1b1324b 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -2805,6 +2805,20 @@
}
+#ifdef CONFIG_PASN
+static inline int wpa_auth_set_ltf_keyseed(struct wpa_authenticator *wpa_auth,
+ const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ if (!wpa_auth->cb->set_ltf_keyseed)
+ return -1;
+ return wpa_auth->cb->set_ltf_keyseed(wpa_auth->cb_ctx, peer_addr,
+ ltf_keyseed, ltf_keyseed_len);
+}
+#endif /* CONFIG_PASN */
+
+
static inline int wpa_auth_add_sta_ft(struct wpa_authenticator *wpa_auth,
const u8 *addr)
{
@@ -2849,6 +2863,18 @@
sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
return;
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_auth_set_ltf_keyseed(sm->wpa_auth, sm->addr,
+ sm->PTK.ltf_keyseed,
+ sm->PTK.ltf_keyseed_len)) {
+ wpa_printf(MSG_ERROR,
+ "FT: Failed to set LTF keyseed to driver");
+ return;
+ }
+#endif /* CONFIG_PASN */
+
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = true;
sm->tk_already_set = true;
@@ -3210,6 +3236,15 @@
pairwise, kdk_len) < 0)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
+#ifdef CONFIG_PASN
+ if (sm->wpa_auth->conf.secure_ltf &&
+ ieee802_11_rsnx_capab(sm->rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_ltf_keyseed(&sm->PTK, sm->wpa_key_mgmt, pairwise)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+#endif /* CONFIG_PASN */
+
sm->pairwise = pairwise;
sm->PTK_valid = true;
sm->tk_already_set = false;
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 9e8dae1..a510952 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -350,6 +350,8 @@
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
+ if (psk_len)
+ *psk_len = sta->sae->pmk_len;
return sta->sae->pmk;
}
if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
@@ -934,7 +936,8 @@
{
struct hostapd_data *hapd = ctx;
- ptksa_cache_add(hapd->ptksa, addr, cipher, life_time, ptk);
+ ptksa_cache_add(hapd->ptksa, hapd->own_addr, addr, cipher, life_time,
+ ptk, NULL, NULL);
}
@@ -1469,6 +1472,21 @@
#endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+static int hostapd_set_ltf_keyseed(void *ctx, const u8 *peer_addr,
+ const u8 *ltf_keyseed,
+ size_t ltf_keyseed_len)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_set_secure_ranging_ctx(hapd, hapd->own_addr,
+ peer_addr, 0, 0, NULL,
+ ltf_keyseed_len,
+ ltf_keyseed, 0);
+}
+#endif /* CONFIG_PASN */
+
+
int hostapd_setup_wpa(struct hostapd_data *hapd)
{
struct wpa_auth_config _conf;
@@ -1515,6 +1533,9 @@
#ifndef CONFIG_NO_RADIUS
.request_radius_psk = hostapd_request_radius_psk,
#endif /* CONFIG_NO_RADIUS */
+#ifdef CONFIG_PASN
+ .set_ltf_keyseed = hostapd_set_ltf_keyseed,
+#endif /* CONFIG_PASN */
};
const u8 *wpa_ie;
size_t wpa_ie_len;
@@ -1551,11 +1572,12 @@
#endif /* CONFIG_OCV */
_conf.secure_ltf =
- !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF);
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_AP);
_conf.secure_rtt =
- !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT);
+ !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_AP);
_conf.prot_range_neg =
- !!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG);
+ !!(hapd->iface->drv_flags2 &
+ WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP);
hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
if (hapd->wpa_auth == NULL) {
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 524922e..1c8affa 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -228,11 +228,21 @@
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
pos += RSN_SELECTOR_LEN;
num_suites++;
}
+ if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
+ pos += RSN_SELECTOR_LEN;
+ num_suites++;
+ }
#endif /* CONFIG_SAE */
if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
@@ -395,7 +405,8 @@
size_t flen;
if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
- (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk)) {
+ (conf->sae_pwe == 1 || conf->sae_pwe == 2 || conf->sae_pk ||
+ wpa_key_mgmt_sae_ext_key(conf->wpa_key_mgmt))) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (conf->sae_pk)
@@ -670,8 +681,12 @@
#ifdef CONFIG_SAE
else if (data.key_mgmt & WPA_KEY_MGMT_SAE)
selector = RSN_AUTH_KEY_MGMT_SAE;
+ else if (data.key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+ selector = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE)
selector = RSN_AUTH_KEY_MGMT_FT_SAE;
+ else if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ selector = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
#endif /* CONFIG_SAE */
else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X)
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
@@ -778,8 +793,12 @@
#ifdef CONFIG_SAE
else if (key_mgmt & WPA_KEY_MGMT_SAE)
sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+ else if (key_mgmt & WPA_KEY_MGMT_SAE_EXT_KEY)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_SAE_EXT_KEY;
else if (key_mgmt & WPA_KEY_MGMT_FT_SAE)
sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE;
+ else if (key_mgmt & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
#endif /* CONFIG_SAE */
else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X;
diff --git a/src/common/defs.h b/src/common/defs.h
index 4e63053..3e658cb 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -50,12 +50,15 @@
#define WPA_KEY_MGMT_DPP BIT(23)
#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
#define WPA_KEY_MGMT_PASN BIT(25)
+#define WPA_KEY_MGMT_SAE_EXT_KEY BIT(26)
+#define WPA_KEY_MGMT_FT_SAE_EXT_KEY BIT(27)
#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
WPA_KEY_MGMT_FT_IEEE8021X | \
WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \
WPA_KEY_MGMT_FT_SAE | \
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY | \
WPA_KEY_MGMT_FT_FILS_SHA256 | \
WPA_KEY_MGMT_FT_FILS_SHA384)
@@ -88,7 +91,9 @@
WPA_KEY_MGMT_FT_PSK |
WPA_KEY_MGMT_PSK_SHA256 |
WPA_KEY_MGMT_SAE |
- WPA_KEY_MGMT_FT_SAE));
+ WPA_KEY_MGMT_SAE_EXT_KEY |
+ WPA_KEY_MGMT_FT_SAE |
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY));
}
static inline int wpa_key_mgmt_ft(int akm)
@@ -111,7 +116,15 @@
static inline int wpa_key_mgmt_sae(int akm)
{
return !!(akm & (WPA_KEY_MGMT_SAE |
- WPA_KEY_MGMT_FT_SAE));
+ WPA_KEY_MGMT_SAE_EXT_KEY |
+ WPA_KEY_MGMT_FT_SAE |
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY));
+}
+
+static inline int wpa_key_mgmt_sae_ext_key(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_SAE_EXT_KEY |
+ WPA_KEY_MGMT_FT_SAE_EXT_KEY));
}
static inline int wpa_key_mgmt_fils(int akm)
@@ -168,6 +181,13 @@
return akm == WPA_KEY_MGMT_CCKM;
}
+static inline int wpa_key_mgmt_cross_akm(int akm)
+{
+ return !!(akm & (WPA_KEY_MGMT_PSK |
+ WPA_KEY_MGMT_PSK_SHA256 |
+ WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_SAE_EXT_KEY));
+}
#define WPA_PROTO_WPA BIT(0)
#define WPA_PROTO_RSN BIT(1)
@@ -498,4 +518,6 @@
FRAME_ENCRYPTED = 1
};
+#define MAX_NUM_MLD_LINKS 15
+
#endif /* DEFS_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
index fcc5241..559bdcd 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -969,7 +969,9 @@
struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
const char *name,
enum dpp_netrole netrole,
- const char *mud_url, int *opclasses)
+ const char *mud_url, int *opclasses,
+ const char *extra_name,
+ const char *extra_value)
{
size_t len, name_len;
const char *tech = "infra";
@@ -992,6 +994,8 @@
len = 100 + name_len * 6 + 1 + int_array_len(opclasses) * 4;
if (mud_url && mud_url[0])
len += 10 + os_strlen(mud_url);
+ if (extra_name && extra_value && extra_name[0] && extra_value[0])
+ len += 10 + os_strlen(extra_name) + os_strlen(extra_value);
#ifdef CONFIG_DPP2
if (auth->csr) {
size_t csr_len;
@@ -1031,6 +1035,10 @@
json_value_sep(json);
json_add_string(json, "pkcs10", csr);
}
+ if (extra_name && extra_value && extra_name[0] && extra_value[0]) {
+ json_value_sep(json);
+ wpabuf_printf(json, "\"%s\":%s", extra_name, extra_value);
+ }
json_end_object(json);
buf = dpp_build_conf_req(auth, wpabuf_head(json));
@@ -1144,6 +1152,8 @@
str_clear_free(conf->passphrase);
os_free(conf->group_id);
os_free(conf->csrattrs);
+ os_free(conf->extra_name);
+ os_free(conf->extra_value);
bin_clear_free(conf, sizeof(*conf));
}
@@ -1270,6 +1280,29 @@
os_memcpy(conf->csrattrs, pos, len);
}
+ pos = os_strstr(cmd, " conf_extra_name=");
+ if (pos) {
+ pos += 17;
+ end = os_strchr(pos, ' ');
+ len = end ? (size_t) (end - pos) : os_strlen(pos);
+ conf->extra_name = os_zalloc(len + 1);
+ if (!conf->extra_name)
+ goto fail;
+ os_memcpy(conf->extra_name, pos, len);
+ }
+
+ pos = os_strstr(cmd, " conf_extra_value=");
+ if (pos) {
+ pos += 18;
+ end = os_strchr(pos, ' ');
+ len = end ? (size_t) (end - pos) : os_strlen(pos);
+ len /= 2;
+ conf->extra_value = os_zalloc(len + 1);
+ if (!conf->extra_value ||
+ hexstr2bin(pos, (u8 *) conf->extra_value, len) < 0)
+ goto fail;
+ }
+
if (!dpp_configuration_valid(conf))
goto fail;
@@ -1582,6 +1615,32 @@
}
+static bool dpp_supports_curve(const char *curve, struct dpp_bootstrap_info *bi)
+{
+ enum dpp_bootstrap_supported_curves idx;
+
+ if (!bi || !bi->supported_curves)
+ return true; /* no support indication available */
+
+ if (os_strcmp(curve, "prime256v1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_P_256;
+ else if (os_strcmp(curve, "secp384r1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_P_384;
+ else if (os_strcmp(curve, "secp521r1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_P_521;
+ else if (os_strcmp(curve, "brainpoolP256r1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_BP_256;
+ else if (os_strcmp(curve, "brainpoolP384r1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_BP_384;
+ else if (os_strcmp(curve, "brainpoolP512r1") == 0)
+ idx = DPP_BOOTSTRAP_CURVE_BP_512;
+ else
+ return true;
+
+ return bi->supported_curves & BIT(idx);
+}
+
+
static struct wpabuf *
dpp_build_conf_obj_dpp(struct dpp_authentication *auth,
struct dpp_configuration *conf)
@@ -1603,10 +1662,23 @@
goto fail;
}
curve = auth->conf->curve;
+ if (dpp_akm_dpp(conf->akm) &&
+ !dpp_supports_curve(curve->name, auth->peer_bi)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Enrollee does not support C-sign-key curve (%s) - cannot generate config object",
+ curve->name);
+ goto fail;
+ }
if (auth->new_curve && auth->new_key_received)
nak_curve = auth->new_curve;
else
nak_curve = auth->curve;
+ if (!dpp_supports_curve(nak_curve->name, auth->peer_bi)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Enrollee does not support netAccessKey curve (%s) - cannot generate config object",
+ nak_curve->name);
+ goto fail;
+ }
akm = conf->akm;
if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
@@ -1663,6 +1735,13 @@
if (auth->conf->net_access_key_curve &&
auth->curve != auth->conf->net_access_key_curve &&
!auth->new_key_received) {
+ if (!dpp_supports_curve(auth->conf->net_access_key_curve->name,
+ auth->peer_bi)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Enrollee does not support the required netAccessKey curve (%s) - cannot generate config object",
+ auth->conf->net_access_key_curve->name);
+ goto fail;
+ }
wpa_printf(MSG_DEBUG,
"DPP: Peer protocol key curve (%s) does not match the required netAccessKey curve (%s) - %s",
auth->curve->name,
@@ -1725,6 +1804,9 @@
tailroom += os_strlen(auth->trusted_eap_server_name);
tailroom += 1000;
}
+ if (conf->extra_name && conf->extra_value)
+ tailroom += 10 + os_strlen(conf->extra_name) +
+ os_strlen(conf->extra_value);
buf = dpp_build_conf_start(auth, conf, tailroom);
if (!buf)
goto fail;
@@ -1785,6 +1867,11 @@
#endif /* CONFIG_DPP2 */
json_end_object(buf);
+ if (conf->extra_name && conf->extra_value) {
+ json_value_sep(buf);
+ wpabuf_printf(buf, "\"%s\":%s", conf->extra_name,
+ conf->extra_value);
+ }
json_end_object(buf);
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object",
@@ -1822,8 +1909,12 @@
{
struct wpabuf *buf;
const char *akm_str;
+ size_t len = 1000;
- buf = dpp_build_conf_start(auth, conf, 1000);
+ if (conf->extra_name && conf->extra_value)
+ len += 10 + os_strlen(conf->extra_name) +
+ os_strlen(conf->extra_value);
+ buf = dpp_build_conf_start(auth, conf, len);
if (!buf)
return NULL;
@@ -1836,6 +1927,11 @@
json_value_sep(buf);
dpp_build_legacy_cred_params(buf, conf);
json_end_object(buf);
+ if (conf->extra_name && conf->extra_value) {
+ json_value_sep(buf);
+ wpabuf_printf(buf, "\"%s\":%s", conf->extra_name,
+ conf->extra_value);
+ }
json_end_object(buf);
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
@@ -4055,7 +4151,7 @@
struct json_token *root = NULL, *netkey, *token;
struct json_token *own_root = NULL;
enum dpp_status_error ret = 255, res;
- struct crypto_ec_key *own_key = NULL, *peer_key = NULL;
+ struct crypto_ec_key *own_key = NULL;
struct wpabuf *own_key_pub = NULL;
const struct dpp_curve_params *curve, *own_curve;
struct dpp_signed_connector_info info;
@@ -4128,12 +4224,12 @@
goto fail;
}
- peer_key = dpp_parse_jwk(netkey, &curve);
- if (!peer_key) {
+ intro->peer_key = dpp_parse_jwk(netkey, &curve);
+ if (!intro->peer_key) {
ret = DPP_STATUS_INVALID_CONNECTOR;
goto fail;
}
- dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+ dpp_debug_print_key("DPP: Received netAccessKey", intro->peer_key);
if (own_curve != curve) {
wpa_printf(MSG_DEBUG,
@@ -4144,7 +4240,7 @@
}
/* ECDH: N = nk * PK */
- if (dpp_ecdh(own_key, peer_key, Nx, &Nx_len) < 0)
+ if (dpp_ecdh(own_key, intro->peer_key, Nx, &Nx_len) < 0)
goto fail;
wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
@@ -4158,26 +4254,45 @@
intro->pmk_len = curve->hash_len;
/* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
- if (dpp_derive_pmkid(curve, own_key, peer_key, intro->pmkid) < 0) {
+ if (dpp_derive_pmkid(curve, own_key, intro->peer_key, intro->pmkid) <
+ 0) {
wpa_printf(MSG_ERROR, "DPP: Failed to derive PMKID");
goto fail;
}
+#ifdef CONFIG_DPP3
+ if (dpp_hpke_suite(curve->ike_group, &intro->kem_id, &intro->kdf_id,
+ &intro->aead_id) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Unsupported group %d",
+ curve->ike_group);
+ goto fail;
+ }
+#endif /* CONFIG_DPP3 */
+
ret = DPP_STATUS_OK;
fail:
if (ret != DPP_STATUS_OK)
- os_memset(intro, 0, sizeof(*intro));
+ dpp_peer_intro_deinit(intro);
os_memset(Nx, 0, sizeof(Nx));
os_free(info.payload);
crypto_ec_key_deinit(own_key);
wpabuf_free(own_key_pub);
- crypto_ec_key_deinit(peer_key);
json_free(root);
json_free(own_root);
return ret;
}
+void dpp_peer_intro_deinit(struct dpp_introduction *intro)
+{
+ if (!intro)
+ return;
+
+ crypto_ec_key_deinit(intro->peer_key);
+ os_memset(intro, 0, sizeof(*intro));
+}
+
+
#ifdef CONFIG_DPP3
int dpp_get_connector_version(const char *connector)
{
@@ -4895,6 +5010,7 @@
#ifdef CONFIG_DPP2
dl_list_init(&dpp->controllers);
dl_list_init(&dpp->tcp_init);
+ dpp->relay_sock = -1;
#endif /* CONFIG_DPP2 */
return dpp;
@@ -4955,3 +5071,98 @@
}
#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_DPP3
+
+struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *msg;
+ const u8 *r_hash = bi->pubkey_hash_chirp;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Build Push Button Presence Announcement frame");
+
+ msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT,
+ 4 + SHA256_MAC_LEN);
+ if (!msg)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_hash);
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Push Button Presence Announcement frame attributes",
+ msg);
+ return msg;
+}
+
+
+struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi,
+ const u8 *e_hash,
+ const u8 *c_nonce,
+ size_t c_nonce_len)
+{
+ struct wpabuf *msg;
+ const u8 *i_hash = bi->pubkey_hash_chirp;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Build Push Button Presence Announcement Response frame");
+
+ msg = dpp_alloc_msg(DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP,
+ 2 * (4 + SHA256_MAC_LEN) + 4 + c_nonce_len);
+ if (!msg)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, e_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ e_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Initiator Bootstrapping Key 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, i_hash, SHA256_MAC_LEN);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, e_hash);
+
+ /* Configurator Nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(msg, c_nonce_len);
+ wpabuf_put_data(msg, c_nonce, c_nonce_len);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Push Button Presence Announcement Response frame attributes",
+ msg);
+ return msg;
+}
+
+#endif /* CONFIG_DPP3 */
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 1241668..ee29a08 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -56,6 +56,11 @@
DPP_PA_RECONFIG_AUTH_RESP = 16,
DPP_PA_RECONFIG_AUTH_CONF = 17,
DPP_PA_PKEX_EXCHANGE_REQ = 18,
+ DPP_PA_PB_PRESENCE_ANNOUNCEMENT = 19,
+ DPP_PA_PB_PRESENCE_ANNOUNCEMENT_RESP = 20,
+ DPP_PA_PRIV_PEER_INTRO_QUERY = 21,
+ DPP_PA_PRIV_PEER_INTRO_NOTIFY = 22,
+ DPP_PA_PRIV_PEER_INTRO_UPDATE = 23,
};
enum dpp_attribute_id {
@@ -181,6 +186,9 @@
int nfc_negotiated; /* whether this has been used in NFC negotiated
* connection handover */
char *configurator_params;
+ u8 peer_pubkey_hash[SHA256_MAC_LEN]; /* for enforcing a specific
+ * peer bootstrapping key with
+ * PKEX */
};
#define PKEX_COUNTER_T_LIMIT 5
@@ -203,6 +211,7 @@
u8 peer_mac[ETH_ALEN];
char *identifier;
char *code;
+ size_t code_len;
struct crypto_ec_key *x;
struct crypto_ec_key *y;
u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
@@ -216,6 +225,7 @@
unsigned int exch_req_tries;
unsigned int freq;
u8 peer_version;
+ struct wpabuf *enc_key;
};
enum dpp_akm {
@@ -254,6 +264,8 @@
int psk_set;
char *csrattrs;
+ char *extra_name;
+ char *extra_value;
};
struct dpp_asymmetric_key {
@@ -411,6 +423,10 @@
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
int peer_version;
+ struct crypto_ec_key *peer_key;
+ enum hpke_kem_id kem_id;
+ enum hpke_kdf_id kdf_id;
+ enum hpke_aead_id aead_id;
};
struct dpp_relay_config {
@@ -437,6 +453,13 @@
bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth);
};
+#define DPP_PB_INFO_COUNT 2
+
+struct dpp_pb_info {
+ u8 hash[SHA256_MAC_LEN];
+ struct os_reltime rx_time;
+};
+
#ifdef CONFIG_TESTING_OPTIONS
enum dpp_test_behavior {
DPP_TEST_DISABLED = 0,
@@ -537,6 +560,9 @@
DPP_TEST_INVALID_PROTOCOL_VERSION_PEER_DISC_RESP = 95,
DPP_TEST_INVALID_PROTOCOL_VERSION_RECONFIG_AUTH_REQ = 96,
DPP_TEST_NO_PROTOCOL_VERSION_RECONFIG_AUTH_REQ = 97,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_REQ = 98,
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_PB_RESP = 99,
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_PB_RESP = 100,
};
extern enum dpp_test_behavior dpp_test;
@@ -583,7 +609,9 @@
struct wpabuf * dpp_build_conf_req_helper(struct dpp_authentication *auth,
const char *name,
enum dpp_netrole netrole,
- const char *mud_url, int *opclasses);
+ const char *mud_url, int *opclasses,
+ const char *extra_name,
+ const char *extra_value);
int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
int dpp_notify_new_qr_code(struct dpp_authentication *auth,
@@ -591,6 +619,8 @@
void dpp_controller_pkex_add(struct dpp_global *dpp,
struct dpp_bootstrap_info *bi,
const char *code, const char *identifier);
+bool dpp_controller_is_own_pkex_req(struct dpp_global *dpp,
+ const u8 *buf, size_t len);
struct dpp_configuration * dpp_configuration_alloc(const char *type);
int dpp_akm_psk(enum dpp_akm akm);
int dpp_akm_sae(enum dpp_akm akm);
@@ -643,17 +673,18 @@
const u8 *csign_key, size_t csign_key_len,
const u8 *peer_connector, size_t peer_connector_len,
os_time_t *expiry);
+void dpp_peer_intro_deinit(struct dpp_introduction *intro);
int dpp_get_connector_version(const char *connector);
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier, const char *code,
- bool v2);
+ size_t code_len, bool v2);
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 char *code, size_t code_len,
const u8 *buf, size_t len, bool v2);
struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
const u8 *peer_mac,
@@ -680,6 +711,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 crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len);
+int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
+ enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id);
+
struct wpabuf * dpp_build_csr(struct dpp_authentication *auth,
const char *name);
int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr);
@@ -716,12 +752,18 @@
const u8 *kid);
int dpp_relay_add_controller(struct dpp_global *dpp,
struct dpp_relay_config *config);
+void dpp_relay_remove_controller(struct dpp_global *dpp,
+ const struct hostapd_ip_addr *addr);
+int dpp_relay_listen(struct dpp_global *dpp, int port,
+ struct dpp_relay_config *config);
+void dpp_relay_stop_listen(struct dpp_global *dpp);
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,
void *cb_ctx);
int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
size_t data_len);
+bool dpp_relay_controller_available(struct dpp_global *dpp);
int dpp_controller_start(struct dpp_global *dpp,
struct dpp_controller_config *config);
int dpp_controller_set_params(struct dpp_global *dpp,
@@ -739,15 +781,20 @@
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 char *name, enum dpp_netrole netrole, void *msg_ctx,
- void *cb_ctx,
+ const char *name, enum dpp_netrole netrole,
+ const char *mud_url,
+ const char *extra_conf_req_name,
+ const char *extra_conf_req_value,
+ void *msg_ctx, void *cb_ctx,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth),
bool (*tcp_msg_sent)(void *ctx,
struct dpp_authentication *auth));
int dpp_tcp_auth(struct dpp_global *dpp, void *_conn,
struct dpp_authentication *auth, const char *name,
- enum dpp_netrole netrole,
+ enum dpp_netrole netrole, const char *mud_url,
+ const char *extra_conf_req_name,
+ const char *extra_conf_req_value,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth),
bool (*tcp_msg_sent)(void *ctx,
@@ -762,6 +809,12 @@
void dpp_notify_chirp_received(void *msg_ctx, int id, const u8 *src,
unsigned int freq, const u8 *hash);
+struct wpabuf * dpp_build_pb_announcement(struct dpp_bootstrap_info *bi);
+struct wpabuf * dpp_build_pb_announcement_resp(struct dpp_bootstrap_info *bi,
+ const u8 *e_hash,
+ const u8 *c_nonce,
+ size_t c_nonce_len);
+
struct dpp_global_config {
void *cb_ctx;
void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
diff --git a/src/common/dpp_crypto.c b/src/common/dpp_crypto.c
index fb239f7..09d4d8c 100644
--- a/src/common/dpp_crypto.c
+++ b/src/common/dpp_crypto.c
@@ -1437,7 +1437,7 @@
struct crypto_ec_point *
dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
- const char *code, const char *identifier,
+ const char *code, size_t code_len, const char *identifier,
struct crypto_ec **ret_ec)
{
u8 hash[DPP_MAX_HASH_LEN];
@@ -1465,9 +1465,9 @@
len[num_elem] = os_strlen(identifier);
num_elem++;
}
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len);
addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
+ len[num_elem] = code_len;
num_elem++;
if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
goto fail;
@@ -1512,7 +1512,7 @@
struct crypto_ec_point *
dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
- const char *code, const char *identifier,
+ const char *code, size_t code_len, const char *identifier,
struct crypto_ec **ret_ec)
{
u8 hash[DPP_MAX_HASH_LEN];
@@ -1540,9 +1540,9 @@
len[num_elem] = os_strlen(identifier);
num_elem++;
}
- wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, code_len);
addr[num_elem] = (const u8 *) code;
- len[num_elem] = os_strlen(code);
+ len[num_elem] = code_len;
num_elem++;
if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
goto fail;
@@ -1590,7 +1590,7 @@
u8 ver_init, u8 ver_resp,
const u8 *Mx, size_t Mx_len,
const u8 *Nx, size_t Nx_len,
- const char *code,
+ const char *code, size_t code_len,
const u8 *Kx, size_t Kx_len,
u8 *z, unsigned int hash_len)
{
@@ -1615,7 +1615,7 @@
info_len = 2 * ETH_ALEN;
else
info_len = 2;
- info_len += Mx_len + Nx_len + os_strlen(code);
+ info_len += Mx_len + Nx_len + code_len;
info = os_malloc(info_len);
if (!info)
return -1;
@@ -1633,7 +1633,7 @@
pos += Mx_len;
os_memcpy(pos, Nx, Nx_len);
pos += Nx_len;
- os_memcpy(pos, code, os_strlen(code));
+ os_memcpy(pos, code, code_len);
/* HKDF-Expand(PRK, info, L) */
if (hash_len == 32)
@@ -2367,6 +2367,7 @@
#ifdef CONFIG_DPP3
+
int dpp_derive_auth_i(struct dpp_authentication *auth, u8 *auth_i)
{
int ret = -1, res;
@@ -2454,6 +2455,47 @@
wpabuf_free(pex);
return ret;
}
+
+
+int dpp_hpke_suite(int iana_group, enum hpke_kem_id *kem_id,
+ enum hpke_kdf_id *kdf_id, enum hpke_aead_id *aead_id)
+{
+ switch (iana_group) {
+ case 19:
+ *kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
+ *kdf_id = HPKE_KDF_HKDF_SHA256;
+ *aead_id = HPKE_AEAD_AES_128_GCM;
+ return 0;
+ case 20:
+ *kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
+ *kdf_id = HPKE_KDF_HKDF_SHA384;
+ *aead_id = HPKE_AEAD_AES_256_GCM;
+ return 0;
+ case 21:
+ *kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
+ *kdf_id = HPKE_KDF_HKDF_SHA512;
+ *aead_id = HPKE_AEAD_AES_256_GCM;
+ return 0;
+ case 28:
+ *kem_id = HPKE_DHKEM_P256_HKDF_SHA256;
+ *kdf_id = HPKE_KDF_HKDF_SHA256;
+ *aead_id = HPKE_AEAD_AES_128_GCM;
+ return 0;
+ case 29:
+ *kem_id = HPKE_DHKEM_P384_HKDF_SHA384;
+ *kdf_id = HPKE_KDF_HKDF_SHA384;
+ *aead_id = HPKE_AEAD_AES_256_GCM;
+ return 0;
+ case 30:
+ *kem_id = HPKE_DHKEM_P521_HKDF_SHA512;
+ *kdf_id = HPKE_KDF_HKDF_SHA512;
+ *aead_id = HPKE_AEAD_AES_256_GCM;
+ return 0;
+ }
+
+ return -1;
+}
+
#endif /* CONFIG_DPP3 */
diff --git a/src/common/dpp_i.h b/src/common/dpp_i.h
index 10db4e8..dfa4a3c 100644
--- a/src/common/dpp_i.h
+++ b/src/common/dpp_i.h
@@ -19,8 +19,16 @@
struct dl_list configurator; /* struct dpp_configurator */
#ifdef CONFIG_DPP2
struct dl_list controllers; /* struct dpp_relay_controller */
+ struct dpp_relay_controller *tmp_controller;
struct dpp_controller *controller;
struct dl_list tcp_init; /* struct dpp_connection */
+ int relay_sock;
+ void *relay_msg_ctx;
+ void *relay_cb_ctx;
+ void (*relay_tx)(void *ctx, const u8 *addr, unsigned int freq,
+ const u8 *msg, size_t len);
+ void (*relay_gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+ int prot, struct wpabuf *buf);
void *cb_ctx;
int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
bool (*tcp_msg_sent)(void *ctx, struct dpp_authentication *auth);
@@ -97,8 +105,6 @@
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);
-struct crypto_ec_key * dpp_set_keypair(const struct dpp_curve_params **curve,
- const u8 *privkey, size_t privkey_len);
struct crypto_ec_key * 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);
@@ -113,17 +119,17 @@
struct crypto_ec_key *peer_key, u8 *pmkid);
struct crypto_ec_point *
dpp_pkex_derive_Qi(const struct dpp_curve_params *curve, const u8 *mac_init,
- const char *code, const char *identifier,
+ const char *code, size_t code_len, const char *identifier,
struct crypto_ec **ret_ec);
struct crypto_ec_point *
dpp_pkex_derive_Qr(const struct dpp_curve_params *curve, const u8 *mac_resp,
- const char *code, const char *identifier,
+ const char *code, size_t code_len, const char *identifier,
struct crypto_ec **ret_ec);
int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
u8 ver_init, u8 ver_resp,
const u8 *Mx, size_t Mx_len,
const u8 *Nx, size_t Nx_len,
- const char *code,
+ const char *code, size_t code_len,
const u8 *Kx, size_t Kx_len,
u8 *z, unsigned int hash_len);
int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
diff --git a/src/common/dpp_pkex.c b/src/common/dpp_pkex.c
index cf4fb6b..dca0d8d 100644
--- a/src/common/dpp_pkex.c
+++ b/src/common/dpp_pkex.c
@@ -41,7 +41,7 @@
/* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */
Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : pkex->own_mac, pkex->code,
- pkex->identifier, &ec);
+ pkex->code_len, pkex->identifier, &ec);
if (!Qi)
goto fail;
@@ -145,6 +145,8 @@
My = wpabuf_put(msg, curve->prime_len);
if (crypto_ec_point_to_bin(ec, M, Mx, My))
goto fail;
+ wpabuf_free(pkex->enc_key);
+ pkex->enc_key = wpabuf_alloc_copy(Mx, 2 * curve->prime_len);
os_memcpy(pkex->Mx, Mx, curve->prime_len);
@@ -171,7 +173,7 @@
struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
const u8 *own_mac,
const char *identifier, const char *code,
- bool v2)
+ size_t code_len, bool v2)
{
struct dpp_pkex *pkex;
@@ -196,9 +198,10 @@
if (!pkex->identifier)
goto fail;
}
- pkex->code = os_strdup(code);
+ pkex->code = os_memdup(code, code_len);
if (!pkex->code)
goto fail;
+ pkex->code_len = code_len;
pkex->exchange_req = dpp_pkex_build_exchange_req(pkex, v2);
if (!pkex->exchange_req)
goto fail;
@@ -340,7 +343,7 @@
const u8 *own_mac,
const u8 *peer_mac,
const char *identifier,
- const char *code,
+ const char *code, size_t code_len,
const u8 *buf, size_t len, bool v2)
{
const u8 *attr_group, *attr_id, *attr_key;
@@ -437,8 +440,8 @@
}
/* Qi = H([MAC-Initiator |] [identifier |] code) * Pi */
- Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, identifier,
- &ec);
+ Qi = dpp_pkex_derive_Qi(curve, v2 ? NULL : peer_mac, code, code_len,
+ identifier, &ec);
if (!Qi)
goto fail;
@@ -477,9 +480,10 @@
if (!pkex->identifier)
goto fail;
}
- pkex->code = os_strdup(code);
+ pkex->code = os_memdup(code, code_len);
if (!pkex->code)
goto fail;
+ pkex->code_len = code_len;
os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
@@ -495,8 +499,8 @@
goto fail;
/* Qr = H([MAC-Responder |] [identifier |] code) * Pr */
- Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, identifier,
- NULL);
+ Qr = dpp_pkex_derive_Qr(curve, v2 ? NULL : own_mac, code, code_len,
+ identifier, NULL);
if (!Qr)
goto fail;
@@ -550,7 +554,8 @@
pkex->peer_version, DPP_VERSION,
pkex->Mx, curve->prime_len,
pkex->Nx, curve->prime_len, pkex->code,
- Kx, Kx_len, pkex->z, curve->hash_len);
+ pkex->code_len, Kx, Kx_len, pkex->z,
+ curve->hash_len);
os_memset(Kx, 0, Kx_len);
if (res < 0)
goto fail;
@@ -791,7 +796,8 @@
/* Qr = H([MAC-Responder |] [identifier |] code) * Pr */
Qr = dpp_pkex_derive_Qr(curve, pkex->v2 ? NULL : pkex->peer_mac,
- pkex->code, pkex->identifier, &ec);
+ pkex->code, pkex->code_len, pkex->identifier,
+ &ec);
if (!Qr)
goto fail;
@@ -869,7 +875,7 @@
DPP_VERSION, pkex->peer_version,
pkex->Mx, curve->prime_len,
attr_key /* N.x */, attr_key_len / 2,
- pkex->code, Kx, Kx_len,
+ pkex->code, pkex->code_len, Kx, Kx_len,
pkex->z, curve->hash_len);
os_memset(Kx, 0, Kx_len);
if (res < 0)
@@ -1357,6 +1363,8 @@
dpp_bootstrap_info_free(bi);
return NULL;
}
+ os_memcpy(pkex->own_bi->peer_pubkey_hash, bi->pubkey_hash,
+ SHA256_MAC_LEN);
dpp_pkex_free(pkex);
dl_list_add(&dpp->bootstrap, &bi->list);
return bi;
@@ -1375,5 +1383,6 @@
crypto_ec_key_deinit(pkex->peer_bootstrap_key);
wpabuf_free(pkex->exchange_req);
wpabuf_free(pkex->exchange_resp);
+ wpabuf_free(pkex->enc_key);
os_free(pkex);
}
diff --git a/src/common/dpp_tcp.c b/src/common/dpp_tcp.c
index c83fb2d..ff18a99 100644
--- a/src/common/dpp_tcp.c
+++ b/src/common/dpp_tcp.c
@@ -48,6 +48,9 @@
unsigned int gas_comeback_in_progress:1;
u8 gas_dialog_token;
char *name;
+ char *mud_url;
+ char *extra_conf_req_name;
+ char *extra_conf_req_value;
enum dpp_netrole netrole;
};
@@ -118,6 +121,9 @@
dpp_auth_deinit(conn->auth);
dpp_pkex_free(conn->pkex);
os_free(conn->name);
+ os_free(conn->mud_url);
+ os_free(conn->extra_conf_req_name);
+ os_free(conn->extra_conf_req_value);
os_free(conn);
}
@@ -133,6 +139,7 @@
struct dpp_relay_config *config)
{
struct dpp_relay_controller *ctrl;
+ char txt[100];
if (!dpp)
return -1;
@@ -148,6 +155,8 @@
ctrl->cb_ctx = config->cb_ctx;
ctrl->tx = config->tx;
ctrl->gas_resp_tx = config->gas_resp_tx;
+ wpa_printf(MSG_DEBUG, "DPP: Add Relay connection to Controller %s",
+ hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
dl_list_add(&dpp->controllers, &ctrl->list);
return 0;
}
@@ -189,6 +198,31 @@
}
+static struct dpp_relay_controller *
+dpp_relay_controller_get_addr(struct dpp_global *dpp,
+ const struct sockaddr_in *addr)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+ list) {
+ if (ctrl->ipaddr.af == AF_INET &&
+ addr->sin_addr.s_addr == ctrl->ipaddr.u.v4.s_addr)
+ return ctrl;
+ }
+
+ if (dpp->tmp_controller &&
+ dpp->tmp_controller->ipaddr.af == AF_INET &&
+ addr->sin_addr.s_addr == dpp->tmp_controller->ipaddr.u.v4.s_addr)
+ return dpp->tmp_controller;
+
+ return NULL;
+}
+
+
static void dpp_controller_gas_done(struct dpp_connection *conn)
{
struct dpp_authentication *auth = conn->auth;
@@ -214,7 +248,8 @@
return;
}
- wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT "conf_status=%d",
+ auth->conf_resp_status);
dpp_connection_remove(conn);
}
@@ -312,8 +347,10 @@
const char *dpp_name;
dpp_name = conn->name ? conn->name : "Test";
- buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
- NULL);
+ buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole,
+ conn->mud_url, NULL,
+ conn->extra_conf_req_name,
+ conn->extra_conf_req_value);
if (!buf) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
@@ -520,6 +557,31 @@
}
+static struct dpp_connection *
+dpp_relay_match_ctrl(struct dpp_relay_controller *ctrl, const u8 *src,
+ unsigned int freq, u8 type)
+{
+ struct dpp_connection *conn;
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+ return conn;
+ if ((type == DPP_PA_PKEX_EXCHANGE_RESP ||
+ type == DPP_PA_AUTHENTICATION_RESP) &&
+ conn->freq == 0 &&
+ is_broadcast_ether_addr(conn->mac_addr)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Associate this peer to the new Controller initiated connection");
+ os_memcpy(conn->mac_addr, src, ETH_ALEN);
+ conn->freq = freq;
+ return conn;
+ }
+ }
+
+ return NULL;
+}
+
+
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,
@@ -538,12 +600,16 @@
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);
- }
+ conn = dpp_relay_match_ctrl(ctrl, src, freq, type);
+ if (conn)
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+
+ if (dpp->tmp_controller) {
+ conn = dpp_relay_match_ctrl(dpp->tmp_controller, src,
+ freq, type);
+ if (conn)
+ return dpp_relay_tx(conn, hdr, buf, len);
}
}
@@ -579,11 +645,25 @@
}
+static struct dpp_connection *
+dpp_relay_find_conn(struct dpp_relay_controller *ctrl, const u8 *src)
+{
+ struct dpp_connection *conn;
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr, ETH_ALEN) == 0)
+ return conn;
+ }
+
+ return NULL;
+}
+
+
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 dpp_connection *conn = NULL;
struct wpabuf *msg;
/* Check if there is a successfully completed authentication for this
@@ -591,19 +671,15 @@
*/
dl_list_for_each(ctrl, &dpp->controllers,
struct dpp_relay_controller, list) {
- if (found)
+ conn = dpp_relay_find_conn(ctrl, src);
+ if (conn)
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)
+ if (!conn && dpp->tmp_controller)
+ conn = dpp_relay_find_conn(dpp->tmp_controller, src);
+
+ if (!conn)
return -1;
msg = wpabuf_alloc(4 + 1 + data_len);
@@ -622,6 +698,12 @@
}
+bool dpp_relay_controller_available(struct dpp_global *dpp)
+{
+ return dpp && dl_list_len(&dpp->controllers) > 0;
+}
+
+
static void dpp_controller_free(struct dpp_controller *ctrl)
{
struct dpp_connection *conn, *tmp;
@@ -799,8 +881,9 @@
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_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "wait_conn_status=1 conf_resp_status=%d",
+ auth->conf_resp_status);
wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
auth->waiting_conn_status_result = 1;
eloop_cancel_timeout(
@@ -812,7 +895,8 @@
return 0;
}
if (status == DPP_STATUS_OK)
- wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT
+ "conf_resp_status=%d", auth->conf_resp_status);
else
wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
return -1; /* to remove the completed connection */
@@ -1017,6 +1101,7 @@
NULL, NULL,
ctrl->pkex_identifier,
ctrl->pkex_code,
+ os_strlen(ctrl->pkex_code),
buf, len, true);
if (!conn->pkex) {
wpa_printf(MSG_DEBUG,
@@ -1307,6 +1392,8 @@
return -1;
}
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_REQ_RX);
+
pos = msg;
end = msg + len;
@@ -1911,7 +1998,10 @@
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,
+ enum dpp_netrole netrole, const char *mud_url,
+ const char *extra_conf_req_name,
+ const char *extra_conf_req_value,
+ void *msg_ctx, void *cb_ctx,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth),
bool (*tcp_msg_sent)(void *ctx,
@@ -1941,6 +2031,12 @@
conn->process_conf_obj = process_conf_obj;
conn->tcp_msg_sent = tcp_msg_sent;
conn->name = os_strdup(name ? name : "Test");
+ if (mud_url)
+ conn->mud_url = os_strdup(mud_url);
+ if (extra_conf_req_name)
+ conn->extra_conf_req_name = os_strdup(extra_conf_req_name);
+ if (extra_conf_req_value)
+ conn->extra_conf_req_value = os_strdup(extra_conf_req_value);
conn->netrole = netrole;
conn->global = dpp;
conn->auth = auth;
@@ -1987,7 +2083,9 @@
int dpp_tcp_auth(struct dpp_global *dpp, void *_conn,
struct dpp_authentication *auth, const char *name,
- enum dpp_netrole netrole,
+ enum dpp_netrole netrole, const char *mud_url,
+ const char *extra_conf_req_name,
+ const char *extra_conf_req_value,
int (*process_conf_obj)(void *ctx,
struct dpp_authentication *auth),
bool (*tcp_msg_sent)(void *ctx,
@@ -2001,6 +2099,13 @@
conn->tcp_msg_sent = tcp_msg_sent;
os_free(conn->name);
conn->name = os_strdup(name ? name : "Test");
+ os_free(conn->mud_url);
+ conn->mud_url = mud_url ? os_strdup(mud_url) : NULL;
+ os_free(conn->extra_conf_req_name);
+ conn->extra_conf_req_name = extra_conf_req_name ?
+ os_strdup(extra_conf_req_name) : NULL;
+ conn->extra_conf_req_value = extra_conf_req_value ?
+ os_strdup(extra_conf_req_value) : NULL;
conn->netrole = netrole;
conn->auth = auth;
@@ -2203,6 +2308,35 @@
}
+bool dpp_controller_is_own_pkex_req(struct dpp_global *dpp,
+ const u8 *buf, size_t len)
+{
+ struct dpp_connection *conn;
+ const u8 *attr_key = NULL;
+ u16 attr_key_len = 0;
+
+ dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+ if (!conn->pkex || !conn->pkex->enc_key)
+ continue;
+
+ if (!attr_key) {
+ attr_key = dpp_get_attr(buf, len,
+ DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key)
+ return false;
+ }
+
+ if (attr_key_len == wpabuf_len(conn->pkex->enc_key) &&
+ os_memcmp(attr_key, wpabuf_head(conn->pkex->enc_key),
+ attr_key_len) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+
void dpp_tcp_init_flush(struct dpp_global *dpp)
{
struct dpp_connection *conn, *tmp;
@@ -2216,6 +2350,10 @@
static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
{
struct dpp_connection *conn, *tmp;
+ char txt[100];
+
+ wpa_printf(MSG_DEBUG, "DPP: Remove Relay connection to Controller %s",
+ hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
list)
@@ -2236,6 +2374,204 @@
dl_list_del(&ctrl->list);
dpp_relay_controller_free(ctrl);
}
+
+ if (dpp->tmp_controller) {
+ dpp_relay_controller_free(dpp->tmp_controller);
+ dpp->tmp_controller = NULL;
+ }
+}
+
+
+void dpp_relay_remove_controller(struct dpp_global *dpp,
+ const struct hostapd_ip_addr *addr)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return;
+
+ dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+ list) {
+ if (hostapd_ip_equal(&ctrl->ipaddr, addr)) {
+ dl_list_del(&ctrl->list);
+ dpp_relay_controller_free(ctrl);
+ return;
+ }
+ }
+
+ if (dpp->tmp_controller &&
+ hostapd_ip_equal(&dpp->tmp_controller->ipaddr, addr)) {
+ dpp_relay_controller_free(dpp->tmp_controller);
+ dpp->tmp_controller = NULL;
+ }
+}
+
+
+static void dpp_relay_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_global *dpp = eloop_ctx;
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int fd;
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn = NULL;
+
+ wpa_printf(MSG_DEBUG, "DPP: New TCP connection (Relay)");
+
+ fd = accept(dpp->relay_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));
+
+ ctrl = dpp_relay_controller_get_addr(dpp, &addr);
+ if (!ctrl && dpp->tmp_controller &&
+ dl_list_len(&dpp->tmp_controller->conn)) {
+ char txt[100];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Remove a temporaty Controller entry for %s",
+ hostapd_ip_txt(&dpp->tmp_controller->ipaddr,
+ txt, sizeof(txt)));
+ dpp_relay_controller_free(dpp->tmp_controller);
+ dpp->tmp_controller = NULL;
+ }
+ if (!ctrl && !dpp->tmp_controller) {
+ wpa_printf(MSG_DEBUG, "DPP: Add a temporary Controller entry");
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ goto fail;
+ dl_list_init(&ctrl->conn);
+ ctrl->global = dpp;
+ ctrl->ipaddr.af = AF_INET;
+ ctrl->ipaddr.u.v4.s_addr = addr.sin_addr.s_addr;
+ ctrl->msg_ctx = dpp->relay_msg_ctx;
+ ctrl->cb_ctx = dpp->relay_cb_ctx;
+ ctrl->tx = dpp->relay_tx;
+ ctrl->gas_resp_tx = dpp->relay_gas_resp_tx;
+ dpp->tmp_controller = ctrl;
+ }
+ if (!ctrl) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Controller found for that address");
+ goto fail;
+ }
+
+ 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");
+ goto fail;
+ }
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ goto fail;
+
+ conn->global = ctrl->global;
+ conn->relay = ctrl;
+ conn->msg_ctx = ctrl->msg_ctx;
+ conn->cb_ctx = ctrl->global->cb_ctx;
+ os_memset(conn->mac_addr, 0xff, ETH_ALEN);
+ 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_relay_listen(struct dpp_global *dpp, int port,
+ struct dpp_relay_config *config)
+{
+ int s;
+ int on = 1;
+ struct sockaddr_in sin;
+
+ if (dpp->relay_sock >= 0) {
+ wpa_printf(MSG_INFO, "DPP: %s(%d) - relay port already opened",
+ __func__, port);
+ return -1;
+ }
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ wpa_printf(MSG_INFO,
+ "DPP: socket(SOCK_STREAM) failed: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (setsockopt(s, 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(s, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+
+ /* TODO: IPv6 */
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ sin.sin_port = htons(port);
+ if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to bind Relay TCP port: %s",
+ strerror(errno));
+ close(s);
+ return -1;
+ }
+ if (listen(s, 10 /* max backlog */) < 0 ||
+ fcntl(s, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(s, EVENT_TYPE_READ, dpp_relay_tcp_cb, dpp,
+ NULL)) {
+ close(s);
+ return -1;
+ }
+
+ dpp->relay_sock = s;
+ dpp->relay_msg_ctx = config->msg_ctx;
+ dpp->relay_cb_ctx = config->cb_ctx;
+ dpp->relay_tx = config->tx;
+ dpp->relay_gas_resp_tx = config->gas_resp_tx;
+ wpa_printf(MSG_DEBUG, "DPP: Relay started on TCP port %d", port);
+ return 0;
+}
+
+
+void dpp_relay_stop_listen(struct dpp_global *dpp)
+{
+ if (!dpp || dpp->relay_sock < 0)
+ return;
+ eloop_unregister_sock(dpp->relay_sock, EVENT_TYPE_READ);
+ close(dpp->relay_sock);
+ dpp->relay_sock = -1;
}
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index b8d3c54..52dc8df 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -3031,3 +3031,38 @@
? CHAN_WIDTH_80P80 : CHAN_WIDTH_80;
return ap_operation_chan_width;
}
+
+const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type)
+{
+ const struct element *elem;
+
+ if (!ies)
+ return NULL;
+
+ for_each_element_extid(elem, WLAN_EID_EXT_MULTI_LINK, ies, len) {
+ if (elem->datalen >= 2 &&
+ (elem->data[1] & MULTI_LINK_CONTROL_TYPE_MASK) == type)
+ return &elem->id;
+ }
+
+ return NULL;
+}
+
+
+const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len)
+{
+ const size_t mld_addr_pos =
+ 2 /* Control field */ +
+ 1 /* Common Info Length field */;
+ const size_t fixed_len = mld_addr_pos +
+ ETH_ALEN /* MLD MAC Address field */;
+
+ if (len < fixed_len)
+ return NULL;
+
+ if ((buf[0] & MULTI_LINK_CONTROL_TYPE_MASK) !=
+ MULTI_LINK_CONTROL_TYPE_BASIC)
+ return NULL;
+
+ return &buf[mld_addr_pos];
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index 13fd10d..2e81751 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -343,6 +343,8 @@
const u8 *data, u8 len);
struct wpabuf * ieee802_11_defrag(struct ieee802_11_elems *elems,
u8 eid, u8 eid_ext);
+const u8 * get_ml_ie(const u8 *ies, size_t len, u8 type);
+const u8 * get_basic_mle_mld_addr(const u8 *buf, size_t len);
int get_max_nss_capability(struct ieee802_11_elems *elems, int parse_for_rx);
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index ae035f5..4345488 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -495,6 +495,7 @@
#define WLAN_EID_EXT_EHT_CAPABILITIES 108
#define WLAN_EID_EXT_TID_TO_LINK_MAPPING 109
#define WLAN_EID_EXT_MULTI_LINK_TRAFFIC_INDICATION 110
+#define WLAN_EID_EXT_AKM_SUITE_SELECTOR 114
/* Extended Capabilities field */
#define WLAN_EXT_CAPAB_20_40_COEX 0
@@ -610,12 +611,20 @@
#define WLAN_ACTION_ROBUST_AV_STREAMING 19
#define WLAN_ACTION_UNPROTECTED_DMG 20
#define WLAN_ACTION_VHT 21
-#define WLAN_ACTION_S1G 22
-#define WLAN_ACTION_S1G_RELAY 23
+#define WLAN_ACTION_UNPROTECTED_S1G 22
+#define WLAN_ACTION_S1G 23
#define WLAN_ACTION_FLOW_CONTROL 24
#define WLAN_ACTION_CTRL_RESP_MCS_NEG 25
#define WLAN_ACTION_FILS 26
+#define WLAN_ACTION_CDMG 27
+#define WLAN_ACTION_CMMG 28
+#define WLAN_ACTION_GLK 29
+#define WLAN_ACTION_HE 30
+#define WLAN_ACTION_PROTECTED_HE 31
+#define WLAN_ACTION_WUR 32
#define WLAN_ACTION_PROTECTED_FTM 34
+#define WLAN_ACTION_EHT 36
+#define WLAN_ACTION_PROTECTED_EHT 37
#define WLAN_ACTION_VENDOR_SPECIFIC_PROTECTED 126
#define WLAN_ACTION_VENDOR_SPECIFIC 127
/* Note: 128-255 used to report errors by setting category | 0x80 */
@@ -2534,6 +2543,27 @@
u8 optional[EHT_MCS_NSS_CAPAB_LEN + EHT_PPE_THRESH_CAPAB_LEN];
} STRUCT_PACKED;
+/* IEEE P802.11be/D2.1, 9.4.2.312 - Multi-Link element */
+
+/* Figure 9-1002f: Multi-Link Control field */
+#define MULTI_LINK_CONTROL_TYPE_MASK 0x07
+
+/* Table 9-401c: Mult-Link element Type subfield encoding */
+#define MULTI_LINK_CONTROL_TYPE_BASIC 0
+#define MULTI_LINK_CONTROL_TYPE_PROBE_REQ 1
+#define MULTI_LINK_CONTROL_TYPE_RECONF 2
+#define MULTI_LINK_CONTROL_TYPE_TDLS 3
+#define MULTI_LINK_CONTROL_TYPE_PRIOR_ACCESS 4
+
+/* Figure 9-1002g: Presence Bitmap subfield of the Basic Multi-Link element */
+#define BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID 0x10
+#define BASIC_MULTI_LINK_CTRL0_PRES_BSS_PARAM_CH_COUNT 0x20
+#define BASIC_MULTI_LINK_CTRL0_PRES_MSD_INFO 0x40
+#define BASIC_MULTI_LINK_CTRL0_PRES_EML_CAPA 0x80
+
+#define BASIC_MULTI_LINK_CTRL1_PRES_MLD_CAPA 0x01
+#define BASIC_MULTI_LINK_CTRL1_PRES_AP_MLD_ID 0x02
+
/* IEEE P802.11ay/D4.0, 9.4.2.251 - EDMG Operation element */
#define EDMG_BSS_OPERATING_CHANNELS_OFFSET 6
#define EDMG_OPERATING_CHANNEL_WIDTH_OFFSET 7
diff --git a/src/common/ptksa_cache.c b/src/common/ptksa_cache.c
index 8fcb135..aacc425 100644
--- a/src/common/ptksa_cache.c
+++ b/src/common/ptksa_cache.c
@@ -51,7 +51,10 @@
wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
MAC2STR(e->addr));
- ptksa_cache_free_entry(ptksa, e);
+ if (e->cb && e->ctx)
+ e->cb(e);
+ else
+ ptksa_cache_free_entry(ptksa, e);
}
ptksa_cache_set_expiration(ptksa);
@@ -254,10 +257,13 @@
/*
* ptksa_cache_add - Add a PTKSA cache entry
* @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @own_addr: Own MAC address
* @addr: Peer address
* @cipher: The cipher used
* @life_time: The PTK life time in seconds
* @ptk: The PTK
+ * @life_time_expiry_cb: Callback for alternative expiration handling
+ * @ctx: Context pointer to save into e->ctx for the callback
* Returns: Pointer to the added PTKSA cache entry or %NULL on error
*
* This function creates a PTKSA entry and adds it to the PTKSA cache.
@@ -265,12 +271,17 @@
* this entry will be replaced with the new entry.
*/
struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
+ const u8 *own_addr,
const u8 *addr, u32 cipher,
u32 life_time,
- const struct wpa_ptk *ptk)
+ const struct wpa_ptk *ptk,
+ void (*life_time_expiry_cb)
+ (struct ptksa_cache_entry *e),
+ void *ctx)
{
struct ptksa_cache_entry *entry, *tmp, *tmp2 = NULL;
struct os_reltime now;
+ bool set_expiry = false;
if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
return NULL;
@@ -289,6 +300,11 @@
dl_list_init(&entry->list);
os_memcpy(entry->addr, addr, ETH_ALEN);
entry->cipher = cipher;
+ entry->cb = life_time_expiry_cb;
+ entry->ctx = ctx;
+
+ if (own_addr)
+ os_memcpy(entry->own_addr, own_addr, ETH_ALEN);
os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
@@ -302,6 +318,8 @@
}
}
+ if (dl_list_empty(&entry->list))
+ set_expiry = true;
/*
* If the expiration is later then all other or the list is empty
* entries, add it to the end of the list;
@@ -317,5 +335,8 @@
"Added PTKSA cache entry addr=" MACSTR " cipher=%u",
MAC2STR(addr), cipher);
+ if (set_expiry)
+ ptksa_cache_set_expiration(ptksa);
+
return entry;
}
diff --git a/src/common/ptksa_cache.h b/src/common/ptksa_cache.h
index 28ef291..a643a26 100644
--- a/src/common/ptksa_cache.h
+++ b/src/common/ptksa_cache.h
@@ -23,6 +23,9 @@
os_time_t expiration;
u32 cipher;
u8 addr[ETH_ALEN];
+ u8 own_addr[ETH_ALEN];
+ void (*cb)(struct ptksa_cache_entry *e);
+ void *ctx;
};
#ifdef CONFIG_PTKSA_CACHE
@@ -35,9 +38,13 @@
const u8 *addr, u32 cipher);
int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len);
struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
+ const u8 *own_addr,
const u8 *addr, u32 cipher,
u32 life_time,
- const struct wpa_ptk *ptk);
+ const struct wpa_ptk *ptk,
+ void (*cb)
+ (struct ptksa_cache_entry *e),
+ void *ctx);
void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
#else /* CONFIG_PTKSA_CACHE */
@@ -64,8 +71,9 @@
}
static inline struct ptksa_cache_entry *
-ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher,
- u32 life_time, const struct wpa_ptk *ptk)
+ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *own_addr, const u8 *addr,
+ u32 cipher, u32 life_time, const struct wpa_ptk *ptk,
+ void (*cb)(struct ptksa_cache_entry *e), void *ctx)
{
return NULL;
}
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index c9e4675..215ba91 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -2,6 +2,7 @@
* Qualcomm Atheros OUI and vendor specific assignments
* Copyright (c) 2014-2017, Qualcomm Atheros, Inc.
* Copyright (c) 2018-2020, The Linux Foundation
+ * Copyright (c) 2021-2022, Qualcomm Innovation Center, Inc.
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -819,6 +820,38 @@
*
* The attributes used with this command are defined in
* enum qca_wlan_vendor_attr_secure_ranging_ctx.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD: This vendor subcommand is used to
+ * enable/disable offload processing in firmware during system/runtime
+ * suspend for CoAP messages (see RFC7252: The Constrained Application
+ * Protocol) and fetch information of the CoAP messages cached during
+ * offload processing.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_coap_offload.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG: Subcommand to configure
+ * (add, remove, or change) a Stream Classification Service (SCS) rule.
+ *
+ * The attributes used with this event are defined in
+ * enum qca_wlan_vendor_attr_scs_rule_config.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY: Fetch SAR capabilities
+ * supported by the WLAN firmware.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_sar_capability.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_SR: Subcommand used to implement Spatial Reuse
+ * (SR) feature. This command is used by userspace to configure SR
+ * parameters to the driver and to get the SR related parameters and
+ * statistics with synchronous responses from the driver.
+ * The driver also uses this command to send asynchronous events to
+ * userspace to indicate suspend/resume of SR feature and changes
+ * in SR parameters.
+ *
+ * The attributes used with this command are defined in
+ * enum qca_wlan_vendor_attr_sr.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -1019,6 +1052,10 @@
QCA_NL80211_VENDOR_SUBCMD_DRIVER_READY = 214,
QCA_NL80211_VENDOR_SUBCMD_PASN = 215,
QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT = 216,
+ QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD = 217,
+ QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG = 218,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY = 219,
+ QCA_NL80211_VENDOR_SUBCMD_SR = 220,
};
/* Compatibility defines for previously used subcmd names.
@@ -1312,6 +1349,9 @@
enum qca_wlan_vendor_attr_roam_auth {
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+ /* Indicates BSSID of the roamed AP for non-MLO roaming and MLD address
+ * of the roamed AP for MLO roaming.
+ */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
@@ -1356,6 +1396,11 @@
* Defined by enum qca_roam_reason.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
+ /* A nested attribute containing per-link information of all the links
+ * of MLO connection done while roaming. The attributes used inside this
+ * nested attribute are defined in enum qca_wlan_vendor_attr_mlo_links.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS = 15,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
@@ -1660,6 +1705,30 @@
* during the register/unregister of netdev. Create and delete NDP
* interface using NL80211_CMD_NEW_INTERFACE and NL80211_CMD_DEL_INTERFACE
* commands respectively if the driver advertises this capability set.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA: Flag indicates that the device in
+ * station mode supports secure LTF. If NL80211_EXT_FEATURE_SECURE_LTF is
+ * set, then QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP: Flag indicates that the device in AP
+ * mode supports secure LTF. If NL80211_EXT_FEATURE_SECURE_LTF is set, then
+ * QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA: Flag indicates that the device in
+ * station mode supports secure RTT measurement exchange. If
+ * NL80211_EXT_FEATURE_SECURE_RTT is set,
+ * QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP: Flag indicates that the device in AP
+ * mode supports secure RTT measurement exchange. If
+ * NL80211_EXT_FEATURE_SECURE_RTT is set,
+ * QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA: Flag indicates that
+ * the device in station mode supports protection of range negotiation and
+ * measurement management frames. If
+ * NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE is set, then
+ * QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA will be ignored.
+ * @QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP: Flag indicates that
+ * the device in AP mode supports protection of range negotiation and
+ * measurement management frames. If
+ * NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE is set, then
+ * QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP will be ignored.
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -1679,6 +1748,12 @@
QCA_WLAN_VENDOR_FEATURE_CONCURRENT_BAND_SESSIONS = 13,
QCA_WLAN_VENDOR_FEATURE_TWT_ASYNC_SUPPORT = 14,
QCA_WLAN_VENDOR_FEATURE_USE_ADD_DEL_VIRTUAL_INTF_FOR_NDI = 15,
+ QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA = 16,
+ QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP = 17,
+ QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA = 18,
+ QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP = 19,
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA = 20,
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP = 21,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -2699,6 +2774,13 @@
*/
QCA_WLAN_VENDOR_ATTR_CONFIG_AUDIO_DATA_PATH = 82,
+ /*
+ * 8-bit unsigned value. This attribute can be used to configure the
+ * Dedicated Bluetooth Antenna Mode (DBAM) feature. Possible values for
+ * this attribute are defined in the enum qca_wlan_dbam_config.
+ */
+ QCA_WLAN_VENDOR_ATTR_CONFIG_DBAM = 83,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_CONFIG_MAX =
@@ -2714,6 +2796,19 @@
QCA_WLAN_VENDOR_ATTR_CONFIG_BEACON_REPORT_FAIL
/**
+ * enum qca_dbam_config - Specifies DBAM config mode
+ * @QCA_DBAM_DISABLE: Firmware disables DBAM
+ * @QCA_DBAM_ENABLE: Firmware enables DBAM opportunistically when
+ * internal criteria are met.
+ * @QCA_DBAM_FORCE_ENABLE: Firmware enables DBAM forcefully.
+ */
+enum qca_dbam_config {
+ QCA_DBAM_DISABLE = 0,
+ QCA_DBAM_ENABLE = 1,
+ QCA_DBAM_FORCE_ENABLE = 2,
+};
+
+/**
* 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
@@ -4126,6 +4221,22 @@
* Possible values are 0-100.
*/
QCA_WLAN_VENDOR_ATTR_LL_STATS_IFACE_INFO_TS_DUTY_CYCLE = 87,
+ /* Unsigned 32 bit value. The number of Beacon frames which are received
+ * from the associated AP and indicate buffered unicast frame(s) for us
+ * in the TIM element.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TIM_BEACON = 88,
+ /* Unsigned 32 bit value. The total number of Beacon frames received
+ * from the associated AP that have wrongly indicated buffered unicast
+ * traffic in the TIM element for us.
+ * Below scenarios will be considered as wrong TIM element beacon:
+ * 1) The related TIM element is set in the beacon for STA but STA
+ * doesn’t receive any unicast data after this beacon.
+ * 2) The related TIM element is still set in the beacon for STA
+ * after STA has indicated power save exit by QoS Null Data frame.
+ */
+ QCA_WLAN_VENDOR_ATTR_LL_STATS_TIM_BEACON_ERR = 89,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_LL_STATS_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_LL_STATS_MAX =
@@ -5790,7 +5901,7 @@
/* HE 40 with extension channel below */
QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40MINUS = 1 << 30,
/* HE 40 intolerant */
- QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1 << 31,
+ QCA_WLAN_VENDOR_CHANNEL_PROP_FLAG_HE40INTOL = 1U << 31,
};
/**
@@ -8710,6 +8821,16 @@
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_11BE_EMLSR_MODE = 58,
+ /* 8-bit unsigned value to configure the driver to enable/disable the
+ * periodic sounding for Tx beamformer functionality. The default
+ * behavior uses algorithm to do sounding based on packet stats.
+ *
+ * 0 - Default behavior.
+ * 1 - Enable the periodic sounding for Tx beamformer.
+ * This attribute is used for testing purposes.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_BEAMFORMER_PERIODIC_SOUNDING = 59,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -9422,6 +9543,14 @@
* @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF: Optional (u64)
* This field contains absolute TSF value of the time at which the TWT
* session will be resumed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_SP_START_OFFSET: Optional (s32)
+ * This field will be used when device supports Flexible TWT.
+ * This field contains an offset value by which to shift the starting time
+ * of the next service period. The value of offset can be negative or positive.
+ * If provided, this attribute will override
+ * QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME. The units are in microseconds.
+ *
*/
enum qca_wlan_vendor_attr_twt_nudge {
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_INVALID = 0,
@@ -9430,6 +9559,7 @@
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_NEXT_TWT_SIZE = 3,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_MAC_ADDR = 4,
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_WAKE_TIME_TSF = 5,
+ QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_SP_START_OFFSET = 6,
/* keep last */
QCA_WLAN_VENDOR_ATTR_TWT_NUDGE_AFTER_LAST,
@@ -10720,14 +10850,21 @@
* These values are used by attribute %QCA_VENDOR_ATTR_BTC_CHAIN_MODE of
* %QCA_NL80211_VENDOR_SUBCMD_BTC_CHAIN_MODE.
*
- * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4G are shared.
- * @QCA_BTC_CHAIN_SEPARATED: chains of BT and WLAN 2.4G are separated.
+ * @QCA_BTC_CHAIN_SHARED: chains of BT and WLAN 2.4 GHz are shared.
+ * @QCA_BTC_CHAIN_SEPARATED_HYBRID: chains of BT and WLAN 2.4 GHz are
+ * separated, hybrid mode.
+ * @QCA_BTC_CHAIN_SEPARATED_FDD: chains of BT and WLAN 2.4 GHz are
+ * separated, fixed FDD mode.
*/
enum qca_btc_chain_mode {
QCA_BTC_CHAIN_SHARED = 0,
- QCA_BTC_CHAIN_SEPARATED = 1,
+ QCA_BTC_CHAIN_SEPARATED_HYBRID = 1,
+ QCA_BTC_CHAIN_SEPARATED_FDD = 2,
};
+/* deprecated legacy name */
+#define QCA_BTC_CHAIN_SEPARATED QCA_BTC_CHAIN_SEPARATED_HYBRID
+
/**
* enum qca_vendor_attr_btc_chain_mode - Specifies attributes for BT coex
* chain mode.
@@ -11064,6 +11201,18 @@
* This represents the average congestion duration of uplink frames in MAC
* queue in unit of ms. This can be queried either in connected state or
* disconnected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS: Array of u32 nested
+ * values, used in AP mode. This represents the MPDU packet count per MCS
+ * rate value of TX packets. Every index of this nested attribute corresponds
+ * to MCS index, e.g., Index 0 represents MCS0 TX rate. This can be
+ * queried in connected state.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS: Array of u32 nested
+ * values, used in AP mode. This represents the MPDU packet count per MCS
+ * rate value of RX packets. Every index of this nested attribute corresponds
+ * to MCS index, e.g., Index 0 represents MCS0 RX rate. This can be
+ * queried in connected state.
*/
enum qca_wlan_vendor_attr_get_sta_info {
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_INVALID = 0,
@@ -11117,6 +11266,8 @@
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_FAIL_REASON = 48,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_ROAM_INVOKE_FAIL_REASON = 49,
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_UPLINK_DELAY = 50,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_TX_PACKETS = 51,
+ QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_PER_MCS_RX_PACKETS = 52,
/* keep last */
QCA_WLAN_VENDOR_ATTR_GET_STA_INFO_AFTER_LAST,
@@ -12540,4 +12691,874 @@
QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_AFTER_LAST - 1,
};
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_filter - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER
+ * nested attribute. The packets that match a filter will be replied with
+ * attributes configured in %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4:
+ * u32 attribute. Destination IPv4 address in network byte order, the
+ * IPv4 packets with different address will be filtered out.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4_IS_BC:
+ * Flag attribute. If it's present, indicates that
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 is a broadcast
+ * address; while if not, indicates that the address is a unicast/multicast
+ * address.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Destination IPv6 address in network byte order, the IPv6 packets
+ * with different destination address will be filtered out.
+ * This attribute is optional.
+ *
+ * At least one of %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6 must be configured.
+ * Packets on both IPv4 and IPv6 will be processed if both are configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_PORT:
+ * u16 attribute. Destination UDP port, the packets with different destination
+ * UDP port will be filtered out.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET:
+ * u32 attribute. Represents the offset (in UDP payload) of the data
+ * to be matched.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_DATA:
+ * NLA_BINARY attribute, the maximum allowed size is 16 bytes.
+ * Binary data that is compared bit-by-bit against the data (specified
+ * by %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET) in UDP
+ * payload, the packets don't match will be filtered out.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_filter {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4 = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV4_IS_BC = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_IPV6 = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_DEST_PORT = 4,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_OFFSET = 5,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MATCH_DATA = 6,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_FILTER_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_reply - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4:
+ * u32 attribute. Source address (in network byte order) for replying
+ * the matching broadcast/multicast IPv4 packets.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source address (in network byte order) for replying the matching
+ * multicast IPv6 packets.
+ * This attribute is optional.
+ *
+ * For broadcast/multicast offload reply, one of
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6 or both must be
+ * configured according to version of the IP address(es) configured in
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER;
+ * while for unicast case, firmware will take the destination IP address
+ * in the received matching packet as the source address for replying.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER:
+ * Nested attribute. Filter for the received UDP packets, only the matching
+ * packets will be replied and cached.
+ * See enum qca_wlan_vendor_attr_coap_offload_filter for list of supported
+ * attributes.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * CoAP message (UDP payload) to be sent upon receiving matching packets.
+ * Firmware is responsible for adding any necessary protocol headers.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_CACHE_EXPTIME:
+ * u32 attribute. Expiration time in milliseconds of the cached CoAP messages.
+ * A cached message will be dropped by firmware if it's expired.
+ * This attribute is optional. A default value of 40000 will be used in the
+ * absence of it.
+ */
+enum qca_wlan_vendor_attr_coap_offload_reply {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV4 = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_SRC_IPV6 = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_FILTER = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MSG = 4,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_CACHE_EXPTIME = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 - Represents parameters for
+ * CoAP message (UDP) transmitting on IPv4.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_ADDR:
+ * u32 attribute. Source address (in network byte order) for transmitting
+ * packets on IPv4.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_PORT:
+ * u16 attribute. Source UDP port.
+ * This attribute is optional, a random port is taken if it's not present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR:
+ * u32 attribute. Destination IPv4 address (in network byte order).
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_IS_BC:
+ * Flag attribute. If it's present, indicates that
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR is a broadcast
+ * address; while if not, indicates that the address is unicast/multicast
+ * address.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_PORT:
+ * u16 attribute. Destination UDP port.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_SRC_PORT = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_ADDR = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_IS_BC = 4,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_DEST_PORT = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV4_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 - Represents parameters for
+ * CoAP message (UDP) transmitting on IPv6.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_ADDR:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source address (in network byte order) for transmitting packets on IPv6.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_PORT:
+ * u16 attribute. Source UDP port.
+ * This attribute is optional, a random port is taken if it's not present.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_ADDR:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Destination IPv6 address (in network byte order).
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_PORT:
+ * u16 attribute. Destination UDP port.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_ADDR = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_SRC_PORT = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_ADDR = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_DEST_PORT = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_TX_IPV6_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_periodic_tx - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4:
+ * Nested attribute. The IPv4 source/destination address/port for offload
+ * transmitting. See enum qca_wlan_vendor_attr_coap_offload_tx_ipv4 for the list
+ * of supported attributes.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6:
+ * Nested attribute. The IPv6 source/destination address/port for offload
+ * transmitting. See enum qca_wlan_vendor_attr_coap_offload_tx_ipv6 for the list
+ * of supported attributes.
+ * This attribute is optional.
+ *
+ * At least one of %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6 must be configured.
+ * Firmware will transmit the packets on both IPv4 and IPv6 if both are
+ * configured.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_PERIOD:
+ * u32 attribute. Period in milliseconds for the periodic transmitting.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * CoAP message (UDP payload) to be periodically transmitted. Firmware
+ * is responsible for adding any necessary protocol headers.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_periodic_tx {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV4 = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_IPV6 = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_PERIOD = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MSG = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload_cache_info - Attributes used
+ * inside %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES nested attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS:
+ * u64 attribute. Received time (since system booted in microseconds) of
+ * the cached CoAP message.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4:
+ * u32 attribute. Source IPv4 address (in network byte order) of the cached
+ * CoAP message.
+ * This attribute is optional.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6:
+ * NLA_BINARY attribute, length is 16 bytes.
+ * Source IPv6 address (in network byte order) of the cached CoAP message.
+ * This attribute is optional.
+ *
+ * At most and at least one of
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 and
+ * %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6 is given for
+ * an entry.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG:
+ * NLA_BINARY attribute, the maximum allowed size is 1152 bytes.
+ * The cached CoAP message (UDP payload). If the actual message size is
+ * greater than the maximum size, it will be truncated and leaving only
+ * the first 1152 bytes.
+ * This attribute is mandatory.
+ */
+enum qca_wlan_vendor_attr_coap_offload_cache_info {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_TS = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV4 = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_SRC_IPV6 = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MSG = 4,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHE_INFO_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_coap_offload_action - Actions for
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE:
+ * Enable CoAP offload reply.
+ * If it's enabled, firmware will start offload processing on each suspend
+ * and stop on each resume.
+ *
+ * Offload reply on match works as follows:
+ * Reply the packets that match the filter with the given CoAP
+ * message (with necessary protocol headers), increase the CoAP message
+ * ID in the given CoAP message by one for the next use after each successful
+ * transmission, and try to store the information of the received packet,
+ * including the received time, source IP address, and CoAP message (UDP
+ * payload).
+ *
+ * Firmware has a limit to the maximum stored entries, it takes the source IP
+ * address as the key of an entry, and keeps at most one entry for each key.
+ * A packet won't be stored if no entry for the same key is present and the
+ * total number of the existing unexpired entries reaches the maximum value.
+ *
+ * If any configured item is changed, user space should disable offload reply
+ * first and then issue a new enable request.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE:
+ * Disable CoAP offload reply and return information of any cached CoAP
+ * messages.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE:
+ * Enable CoAP offload periodic transmitting.
+ * If it's enabled, firmware will start offload periodic transmitting on
+ * each suspend and stop on each resume.
+ *
+ * Offload periodic transmitting works as follows:
+ * Send the given CoAP message (with necessary protocol headers) with the given
+ * source/destination IP address/UDP port periodically based on the given
+ * period and increase the CoAP message ID in the given CoAP message by one
+ * for the next use after each successful transmission.
+ *
+ * If any configured item is changed, user space should disable offload
+ * periodic transmitting first and then issue a new enable request.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE:
+ * Disable CoAP offload periodic transmitting.
+ *
+ * @QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET:
+ * Get information of the CoAP messages cached during offload reply
+ * processing. The cache is cleared after retrieval.
+ */
+enum qca_wlan_vendor_coap_offload_action {
+ QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE = 0,
+ QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE = 1,
+ QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE = 2,
+ QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_DISABLE = 3,
+ QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET = 4,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_coap_offload - Used by the
+ * vendor command QCA_NL80211_VENDOR_SUBCMD_COAP_OFFLOAD.
+ * This is used to set parameters for CoAP offload processing, or get
+ * cached CoAP messages from firmware.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION:
+ * u32 attribute. Action to take in this vendor command.
+ * See enum qca_wlan_vendor_coap_offload_action for supported actions.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REQ_ID:
+ * u32 attribute. Represents the Request ID for the CoAP offload
+ * configuration, which can help to identify the user entity starting
+ * the CoAP offload processing and accordingly stop the respective
+ * ones/get the cached CoAP messages with the matching ID.
+ * This attribute is mandatory.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY:
+ * Nested attribute. Parameters for offload reply.
+ * See enum qca_wlan_vendor_attr_coap_offload_reply for the list of
+ * supported attributes.
+ * This attribute is mandatory if %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION
+ * is QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_ENABLE, and is ignored
+ * otherwise.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX:
+ * Nested attribute. Parameters for offload periodic transmitting.
+ * See enum qca_wlan_vendor_attr_coap_offload_periodic_tx for the list of
+ * supported attributes.
+ * This attribute is mandatory if %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION is
+ * QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_PERIODIC_TX_ENABLE, and is ignored
+ * otherwise.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES:
+ * Array of nested attributes. Information of the cached CoAP messages,
+ * where each entry is taken from
+ * enum qca_wlan_vendor_attr_coap_offload_cache_info.
+ * This attribute is used for reporting the cached CoAP messages
+ * in reply for command in which %QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION
+ * is QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_CACHE_GET or
+ * QCA_WLAN_VENDOR_COAP_OFFLOAD_ACTION_REPLY_DISABLE. It means there is no
+ * cached item if this attribute is not present.
+ */
+enum qca_wlan_vendor_attr_coap_offload {
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_ACTION = 1,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REQ_ID = 2,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_REPLY = 3,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_PERIODIC_TX = 4,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_CACHES = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_MAX =
+ QCA_WLAN_VENDOR_ATTR_COAP_OFFLOAD_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_scs_rule_config - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_SCS_RULE_CONFIG to configure Stream Classification
+ * Service (SCS) rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_RULE_ID: Mandatory u32 attribute.
+ * Represents the unique id of SCS rule to be configured.
+
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_REQUEST_TYPE: Mandatory u8 attribute.
+ * Represents the request type: add, remove, or change.
+ * Values as defined in IEEE Std 802.11-2020, Table 9-246 (SCS Request
+ * Type definitions).
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_OUTPUT_TID: Mandatory u8 attribute
+ * in case of add/change request type.
+ * Represents the output traffic identifier (TID) to be assigned to the flow
+ * matching the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_CLASSIFIER_TYPE: Mandatory u8
+ * attribute in case of add/change request type.
+ * Represents type of classifier parameters present in SCS rule.
+ * Refer IEEE Std 802.11-2020 Table 9-164 (Frame classifier type).
+ * Only classifier types 4 and 10 are supported for SCS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_VERSION: Mandatory u8 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the IP version (4: IPv4, 6: IPv6) of the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV4_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv4.
+ * Represents the source IPv4 address in the rule which is to be compared
+ * against the source IP address in the IPv4 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV4_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv4.
+ * Represents the destination IPv4 address in the rule which is to be compared
+ * against the destination IP address in the IPv4 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV6_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv6.
+ * Represents the source IPv6 address in the rule which is to be compared
+ * against the source IP address in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV6_ADDR: Optional
+ * attribute in case of add/change request type when classifier type is TCLAS4
+ * and version attribute is IPv6.
+ * Represents the destination IPv6 address in the rule which is to be compared
+ * against the destination IP address in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_PORT: Optional u16 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the source port number in the rule which is to be compared against
+ * the source port number in the protocol header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_PORT: Optional u16 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the destination port number in the rule which is to be compared
+ * against the destination port number in the protocol header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DSCP: Optional u8 attribute
+ * in case of add/change request type when classifier type is TCLAS4.
+ * Represents the DSCP value in the rule which is to be compared against the
+ * DSCP field present in the IP header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_NEXT_HEADER: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS4.
+ * Represents the protocol/next header in the rule which is to be compared
+ * against the protocol/next header field present in the IPv4/IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_FLOW_LABEL: Optional
+ * attribute of size 3 bytes present in case of add/change request type
+ * when classifier type is TCLAS4 and version is IPv6.
+ * Represents the flow label value in the rule which is to be compared against
+ * the flow label field present in the IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_PROTOCOL_INSTANCE: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS10.
+ * Represents the protocol instance number in the rule.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER: Optional u8
+ * attribute in case of add/change request type when classifier type is TCLAS10.
+ * Represents the protocol/next header in the rule which is to be compared
+ * against the protocol/next header field present in the IPv4/IPv6 header.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK: Optional
+ * attribute of variable length present when request type is add/change and
+ * classifier type is TCLAS10.
+ * Represents the mask to be used for masking the header contents of the header
+ * specified by QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER
+ * attribute.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE: Optional
+ * attribute of variable length present when request type is add/change and
+ * classifier type is TCLAS10.
+ * Represents the value to be compared against after masking the header contents
+ * of the header specified by the
+ * QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER attribute with the
+ * filter mask specified by the
+ * QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK attribute.
+ */
+enum qca_wlan_vendor_attr_scs_rule_config {
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_RULE_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_REQUEST_TYPE = 2,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_OUTPUT_TID = 3,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_CLASSIFIER_TYPE = 4,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_VERSION = 5,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV4_ADDR = 6,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV4_ADDR = 7,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_IPV6_ADDR = 8,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_IPV6_ADDR = 9,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_SRC_PORT = 10,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DST_PORT = 11,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_DSCP = 12,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_NEXT_HEADER = 13,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS4_FLOW_LABEL = 14,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_PROTOCOL_INSTANCE = 15,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_NEXT_HEADER = 16,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_MASK = 17,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_TCLAS10_FILTER_VALUE = 18,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_MAX =
+ QCA_WLAN_VENDOR_ATTR_SCS_RULE_CONFIG_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_mlo_links - Definition of attributes used inside
+ * nested attribute QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID: u8 attribute, link ID of this MLO link.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR: Own MAC address of this MLO link.
+ * @QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID: AP link MAC address of this MLO link.
+ */
+enum qca_wlan_vendor_attr_mlo_links {
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID = 1,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR = 2,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID = 3,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX =
+ QCA_WLAN_VENDOR_ATTR_MLO_LINK_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_sar_version - This describes the current SAR version
+ * used in the firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_1: The firmware supports legacy SAR.
+ * In legacy SAR, the firmware supports 5 static and 1 user defined SAR limits.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_2: The firmware supports SAR version 2,
+ * i.e., SAR Non DBS mode. In SAR version 2, the firmware has 6 SAR tables for
+ * each CTL group. So user can select up to 6 SAR indexes from the current CTL
+ * groups.
+ *
+ * @QCA_WLAN_VENDOR_SAR_VERSION_3: The firmware supports SAR version 3,
+ * i.e., SAR DBS mode. In SAR version 3, the firmware has 6 SAR tables for each
+ * CTL group but user can choose up to 3 SAR set index only, as the top half
+ * of the SAR index (0 to 2) is used for non DBS purpose and the bottom half of
+ * the SAR index (3 to 5) is used for DBS mode.
+ */
+enum qca_wlan_vendor_sar_version {
+ QCA_WLAN_VENDOR_SAR_VERSION_INVALID = 0,
+ QCA_WLAN_VENDOR_SAR_VERSION_1 = 1,
+ QCA_WLAN_VENDOR_SAR_VERSION_2 = 2,
+ QCA_WLAN_VENDOR_SAR_VERSION_3 = 3,
+};
+
+/**
+ * enum qca_wlan_vendor_sar_ctl_group_state - This describes whether
+ * CTL grouping is enabled or disabled in the firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_ENABLED: CTL grouping
+ * is enabled in firmware.
+ *
+ * @QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_DISABLED: CTL grouping
+ * is disabled in firmware.
+ *
+ */
+enum qca_wlan_vendor_sar_ctl_group_state {
+ QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_INVALID = 0,
+ QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_ENABLED = 1,
+ QCA_WLAN_VENDOR_SAR_CTL_GROUP_STATE_DISABLED = 2,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sar_capability - Used by the vendor command
+ * QCA_NL80211_VENDOR_SUBCMD_GET_SAR_CAPABILITY to get SAR capabilities
+ * supported by the firmware.
+
+ * @QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION:
+ * u32 attribute. This field describes current SAR version supported by the
+ * firmware.
+ * See enum qca_wlan_vendor_sar_version for more information.
+ * This attribute is mandatory.
+
+ * @QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_CTL_GROUP_STATE:
+ * u32 attribute. This field describes whether CTL groups are enabled
+ * or disabled in the firmware.
+ * See enum qca_wlan_vendor_sar_ctl_group_state for more information.
+ * This attribute is optional.
+ */
+
+enum qca_wlan_vendor_attr_sar_capability {
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_VERSION = 1,
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_CTL_GROUP_STATE = 2,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_MAX =
+ QCA_WLAN_VENDOR_ATTR_SAR_CAPABILITY_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr_stats - Attributes for Spatial Reuse statistics.
+ * These statistics are sent from the driver in a response when userspace
+ * queries to get the statistics using the operation
+ * %QCA_WLAN_SR_OPERATION_GET_STATS. These statistics are reset
+ * by the driver when the SR feature is enabled, when the driver receives
+ * %QCA_WLAN_SR_OPERATION_CLEAR_STATS operation, or when disconnected.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_OPPORTUNITIES_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG TX opportunities.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_TRIED_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG PPDUs tried to transmit.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_SUCCESS_COUNT: u32 attribute.
+ * Mandatory only when non-SRG is supported by the AP and optional otherwise.
+ * This represents the number of non-SRG PPDUs successfully transmitted.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_OPPORTUNITIES_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG TX opportunities.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_TRIED_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG PPDUs tried to transmit.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_SUCCESS_COUNT: u32 attribute.
+ * Mandatory only when SRG is supported by the AP and optional otherwise.
+ * This represents the number of SRG PPDUs successfully transmitted.
+ */
+enum qca_wlan_vendor_attr_sr_stats {
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_OPPORTUNITIES_COUNT = 1,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_TRIED_COUNT = 2,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_NON_SRG_TX_PPDU_SUCCESS_COUNT = 3,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_OPPORTUNITIES_COUNT = 4,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_TRIED_COUNT = 5,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_SRG_TX_PPDU_SUCCESS_COUNT = 6,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_MAX =
+ QCA_WLAN_VENDOR_ATTR_SR_STATS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_sr_reason_code - Defines the different reason codes used in
+ * Spatial Reuse feature.
+ *
+ * @QCA_WLAN_SR_REASON_CODE_ROAMING: The SR feature is disabled/enabled due to
+ * roaming to an AP that doesn't support/supports SR feature, respectively.
+ *
+ * @QCA_WLAN_SR_REASON_CODE_CONCURRENCY: The SR feature is disabled/enabled due
+ * to change in concurrent interfaces that are supported by the driver.
+ */
+enum qca_wlan_sr_reason_code {
+ QCA_WLAN_SR_REASON_CODE_ROAMING = 0,
+ QCA_WLAN_SR_REASON_CODE_CONCURRENCY = 1,
+};
+
+/**
+ * enum qca_wlan_sr_operation - Defines the different types of SR operations.
+ * The values are used inside attribute %QCA_WLAN_VENDOR_ATTR_SR_OPERATION.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_ENABLE: Userspace sends this operation to the
+ * driver to enable the Spatial Reuse feature. Attributes
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD and
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD are used with this
+ * operation.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_DISABLE: Userspace sends this operation to the
+ * driver to disable the Spatial Reuse feature.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_SUSPEND: The driver uses this operation in an
+ * asynchronous event sent to userspace when the SR feature is disabled.
+ * The disable reason is encoded in QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE
+ * and sent along with the asynchronous event.
+ *
+ * @QCA_WLAN_SR_OPERATION_SR_RESUME: The driver uses this operation in an
+ * asynchronous event when the SR feature is enabled again after the SR feature
+ * was suspended by the driver earlier. The enable reason is
+ * encoded in QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE. Attributes used are
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD and
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD.
+ *
+ * @QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT: This operation is
+ * used to prohibit PSR-based spatial reuse and non-SRG OBSS PD-based spatial
+ * reuse transmissions. Userspace sends this operation to the driver.
+ * The driver/firmware upon receiving this operation shall prohibit PSR-based
+ * spatial reuse and non-SRG OBSS PD-based spatial reuse transmissions.
+ *
+ * @QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_ALLOW: This operation is
+ * used to allow PSR-based spatial reuse and non-SRG OBSS PD-based spatial
+ * reuse transmissions. Userspace sends this operation to the driver.
+ * The driver/firmware upon receiving this operation shall allow PSR-based
+ * spatial reuse and non-SRG OBSS PD-based spatial reuse transmissions.
+ *
+ * @QCA_WLAN_SR_OPERATION_GET_STATS: Userspace sends this operation to the
+ * driver to get the SR statistics and the driver sends a synchronous response
+ * with the attributes defined in enum qca_wlan_vendor_attr_sr_stats using the
+ * nested attribute %QCA_WLAN_VENDOR_ATTR_SR_STATS.
+ *
+ * @QCA_WLAN_SR_OPERATION_CLEAR_STATS: Userspace sends this operation to the
+ * driver to clear the SR statistics and upon receiving this operation
+ * the driver/firmware shall clear the SR statistics.
+ *
+ * @QCA_WLAN_SR_OPERATION_GET_PARAMS: Userspace sends this operation to the
+ * driver to get the SR parameters and the driver sends the synchronous response
+ * with the following required attributes:
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW.
+ *
+ * @QCA_WLAN_SR_OPERATION_UPDATE_PARAMS: The driver uses this operation in an
+ * asynchronous event to userspace to update any changes in SR parameters.
+ * The following attributes are used with this operation:
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE,
+ * %QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW.
+ */
+enum qca_wlan_sr_operation {
+ QCA_WLAN_SR_OPERATION_SR_ENABLE = 0,
+ QCA_WLAN_SR_OPERATION_SR_DISABLE = 1,
+ QCA_WLAN_SR_OPERATION_SR_SUSPEND = 2,
+ QCA_WLAN_SR_OPERATION_SR_RESUME = 3,
+ QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT = 4,
+ QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_ALLOW = 5,
+ QCA_WLAN_SR_OPERATION_GET_STATS = 6,
+ QCA_WLAN_SR_OPERATION_CLEAR_STATS = 7,
+ QCA_WLAN_SR_OPERATION_GET_PARAMS = 8,
+ QCA_WLAN_SR_OPERATION_UPDATE_PARAMS = 9,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr_params - Defines attributes for SR configuration
+ * parameters used by attribute %QCA_WLAN_VENDOR_ATTR_SR_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE: Flag attribute.
+ * This attribute is optionally set in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS and in request when operation is set to
+ * %QCA_WLAN_SR_OPERATION_PSR_AND_NON_SRG_OBSS_PD_PROHIBIT. Refer IEEE Std
+ * 802.11ax-2021 Figure 9-788r-SR Control field format to understand more
+ * about HESIGA_Spatial_reuse_value15_allowed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW: Flag attribute.
+ * This attribute is used in response to %QCA_WLAN_SR_OPERATION_GET_PARAMS
+ * operation. This indicates whether non-SRG OBSS PD SR transmissions are
+ * allowed or not at non-AP STAs that are associated with the AP. If present
+ * non-SRG OBSS PD SR transmissions are not allowed else are allowed.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the SRG OBSS PD
+ * Min Offset field which contains an unsigned integer that is added to -82 dBm
+ * to generate the value of the SRG OBSS PD Min parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the SRG OBSS PD
+ * Max Offset field which contains an unsigned integer that is added to -82 dBm
+ * to generate the value of the SRG OBSS PD Max parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET: Optional u8
+ * attribute. This attribute is used in response to
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS operation. This indicates the Non-SRG OBSS
+ * PD Max Offset field which contains an unsigned integer that is added to -82
+ * dBm to generate the value of the Non-SRG OBSS PD Max parameter.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD: s32 attribute (in dBm).
+ * Userspace optionally sends this attribute with
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE operation to the driver to specify the
+ * preferred SRG PD threshold. The driver shall send this attribute to
+ * userspace in SR resume event to indicate the PD threshold being used for SR.
+ * When there is change in SRG PD threshold (for example, due to roaming, etc.)
+ * the driver shall indicate the userspace the newly configured SRG PD threshold
+ * using an asynchronous event.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD: s32 attribute (in dBm).
+ * Userspace optionally sends this attribute with
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE operation to the driver to specify the
+ * preferred non-SRG PD threshold. The driver shall send this attribute to
+ * userspace in SR resume event to indicate the PD threshold being used for SR.
+ * When there is change in non-SRG PD threshold (for example, due to roaming,
+ * etc.) the driver shall indicate the userspace the newly configured non-SRG PD
+ * threshold using an asynchronous event.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE: u32 attribute. The possible
+ * values are defined in enum qca_wlan_sr_reason_code. This
+ * attribute is used with %QCA_WLAN_SR_OPERATION_SR_RESUME and
+ * %QCA_WLAN_SR_OPERATION_SR_SUSPEND operations.
+ */
+enum qca_wlan_vendor_attr_sr_params {
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_HESIGA_VAL15_ENABLE = 1,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_DISALLOW = 2,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MIN_OFFSET = 3,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_OBSS_PD_MAX_OFFSET = 4,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_OBSS_PD_MAX_OFFSET = 5,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_SRG_PD_THRESHOLD = 6,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_NON_SRG_PD_THRESHOLD = 7,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_REASON_CODE = 8,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_MAX =
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS_AFTER_LAST - 1,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_sr - Defines the attributes used by the vendor
+ * command QCA_NL80211_VENDOR_SUBCMD_SR.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_OPERATION: Mandatory u8 attribute for all requests
+ * from userspace to the driver. Possible values are defined in enum
+ * qca_wlan_sr_operation.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_PARAMS: Nested attribute, contains the SR
+ * configuration parameters. The possible attributes inside this attribute are
+ * defined in enum qca_wlan_vendor_attr_sr_params.
+ * This attribute is used when QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to
+ * %QCA_WLAN_SR_OPERATION_SR_ENABLE in requests from userspace to the driver and
+ * also in response from the driver to userspace when the response is sent for
+ * %QCA_WLAN_SR_OPERATION_GET_PARAMS.
+ * The driver uses this attribute in asynchronous events in which the operation
+ * is set to %QCA_WLAN_SR_OPERATION_SR_RESUME,
+ * %QCA_WLAN_SR_OPERATION_SR_SUSPEND or %QCA_WLAN_SR_OPERATION_UPDATE_PARAMS.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_SR_STATS: Nested attribute, contains the SR
+ * statistics. These attributes used inside this are defined in enum
+ * qca_wlan_vendor_attr_sr_stats.
+ * This attribute is used in response from the driver to a command in which
+ * %QCA_WLAN_VENDOR_ATTR_SR_OPERATION is set to
+ * %QCA_WLAN_SR_OPERATION_GET_STATS.
+ */
+enum qca_wlan_vendor_attr_sr {
+ QCA_WLAN_VENDOR_ATTR_SR_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_SR_OPERATION = 1,
+ QCA_WLAN_VENDOR_ATTR_SR_PARAMS = 2,
+ QCA_WLAN_VENDOR_ATTR_SR_STATS = 3,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_SR_MAX =
+ QCA_WLAN_VENDOR_ATTR_SR_AFTER_LAST - 1,
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index c0f154e..33c7e5f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,8 @@
#include "includes.h"
#include "common.h"
+#include "common/defs.h"
+#include "common/wpa_common.h"
#include "utils/const_time.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
@@ -1520,10 +1522,11 @@
const u8 *salt;
struct wpabuf *rejected_groups = NULL;
u8 keyseed[SAE_MAX_HASH_LEN];
- u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN];
+ u8 keys[2 * SAE_MAX_HASH_LEN + SAE_PMK_LEN_MAX];
struct crypto_bignum *tmp;
int ret = -1;
size_t hash_len, salt_len, prime_len = sae->tmp->prime_len;
+ size_t pmk_len;
const u8 *addr[1];
size_t len[1];
@@ -1545,6 +1548,14 @@
hash_len = sae_ffc_prime_len_2_hash_len(prime_len);
else
hash_len = sae_ecc_prime_len_2_hash_len(prime_len);
+ if (wpa_key_mgmt_sae_ext_key(sae->akmp))
+ pmk_len = hash_len;
+ else
+ pmk_len = SAE_PMK_LEN;
+ wpa_printf(MSG_DEBUG, "SAE: Derive keys - H2E=%d AKMP=0x%x = %08x (%s)",
+ sae->h2e, sae->akmp,
+ wpa_akm_to_suite(sae->akmp),
+ wpa_key_mgmt_txt(sae->akmp, WPA_PROTO_RSN));
if (sae->h2e && (sae->tmp->own_rejected_groups ||
sae->tmp->peer_rejected_groups)) {
struct wpabuf *own, *peer;
@@ -1602,25 +1613,26 @@
if (sae->pk) {
if (sae_kdf_hash(hash_len, keyseed, "SAE-PK keys",
val, sae->tmp->order_len,
- keys, 2 * hash_len + SAE_PMK_LEN) < 0)
+ keys, 2 * hash_len + pmk_len) < 0)
goto fail;
} else {
if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
val, sae->tmp->order_len,
- keys, hash_len + SAE_PMK_LEN) < 0)
+ keys, hash_len + pmk_len) < 0)
goto fail;
}
#else /* CONFIG_SAE_PK */
if (sae_kdf_hash(hash_len, keyseed, "SAE KCK and PMK",
val, sae->tmp->order_len,
- keys, hash_len + SAE_PMK_LEN) < 0)
+ keys, hash_len + 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->pmk, keys + hash_len, pmk_len);
+ sae->pmk_len = pmk_len;
os_memcpy(sae->pmkid, val, SAE_PMKID_LEN);
#ifdef CONFIG_SAE_PK
if (sae->pk) {
@@ -1634,7 +1646,7 @@
forced_memzero(keys, sizeof(keys));
wpa_hexdump_key(MSG_DEBUG, "SAE: KCK",
sae->tmp->kck, sae->tmp->kck_len);
- wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, sae->pmk_len);
ret = 0;
fail:
@@ -1726,6 +1738,17 @@
token);
}
+ if (wpa_key_mgmt_sae_ext_key(sae->akmp)) {
+ u32 suite = wpa_akm_to_suite(sae->akmp);
+
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + RSN_SELECTOR_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_AKM_SUITE_SELECTOR);
+ RSN_SELECTOR_PUT(wpabuf_put(buf, RSN_SELECTOR_LEN), suite);
+ wpa_printf(MSG_DEBUG, "SAE: AKM Suite Selector: %08x", suite);
+ sae->own_akm_suite_selector = suite;
+ }
+
return 0;
}
@@ -1802,6 +1825,16 @@
}
+static int sae_is_akm_suite_selector_elem(const u8 *pos, const u8 *end)
+{
+ return end - pos >= 2 + 1 + RSN_SELECTOR_LEN &&
+ pos[0] == WLAN_EID_EXTENSION &&
+ pos[1] >= 1 + RSN_SELECTOR_LEN &&
+ end - pos - 2 >= pos[1] &&
+ pos[2] == WLAN_EID_EXT_AKM_SUITE_SELECTOR;
+}
+
+
static void sae_parse_commit_token(struct sae_data *sae, const u8 **pos,
const u8 *end, const u8 **token,
size_t *token_len, int h2e)
@@ -2089,6 +2122,35 @@
}
+static int sae_parse_akm_suite_selector(struct sae_data *sae,
+ const u8 **pos, const u8 *end)
+{
+ const u8 *epos;
+ u8 len;
+
+ wpa_hexdump(MSG_DEBUG, "SAE: Possible elements at the end of the frame",
+ *pos, end - *pos);
+ if (!sae_is_akm_suite_selector_elem(*pos, end))
+ return WLAN_STATUS_SUCCESS;
+
+ epos = *pos;
+ epos++; /* skip IE type */
+ len = *epos++; /* IE length */
+ if (len > end - epos || len < 1)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ epos++; /* skip ext ID */
+ len--;
+
+ if (len < RSN_SELECTOR_LEN)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ sae->peer_akm_suite_selector = RSN_SELECTOR_GET(epos);
+ wpa_printf(MSG_DEBUG, "SAE: Received AKM Suite Selector: %08x",
+ sae->peer_akm_suite_selector);
+ *pos = epos + len;
+ return WLAN_STATUS_SUCCESS;
+}
+
+
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)
@@ -2133,6 +2195,31 @@
if (h2e)
sae_parse_token_container(sae, pos, end, token, token_len);
+ /* Conditional AKM Suite Selector element */
+ if (h2e) {
+ res = sae_parse_akm_suite_selector(sae, &pos, end);
+ if (res != WLAN_STATUS_SUCCESS)
+ return res;
+ }
+
+ if (sae->own_akm_suite_selector &&
+ sae->own_akm_suite_selector != sae->peer_akm_suite_selector) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: AKM suite selector mismatch: own=%08x peer=%08x",
+ sae->own_akm_suite_selector,
+ sae->peer_akm_suite_selector);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (!sae->akmp) {
+ if (sae->peer_akm_suite_selector ==
+ RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+ sae->akmp = WPA_KEY_MGMT_SAE_EXT_KEY;
+ else if (sae->peer_akm_suite_selector ==
+ RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+ sae->akmp = WPA_KEY_MGMT_FT_SAE_EXT_KEY;
+ }
+
/*
* Check whether peer-commit-scalar and PEER-COMMIT-ELEMENT are same as
* the values we sent which would be evidence of a reflection attack.
diff --git a/src/common/sae.h b/src/common/sae.h
index 93fc5fb..6a6b0c8 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -11,6 +11,7 @@
#define SAE_KCK_LEN 32
#define SAE_PMK_LEN 32
+#define SAE_PMK_LEN_MAX 64
#define SAE_PMKID_LEN 16
#define SAE_MAX_PRIME_LEN 512
#define SAE_MAX_ECC_PRIME_LEN 66
@@ -104,7 +105,11 @@
struct sae_data {
enum sae_state state;
u16 send_confirm;
- u8 pmk[SAE_PMK_LEN];
+ u8 pmk[SAE_PMK_LEN_MAX];
+ size_t pmk_len;
+ int akmp; /* WPA_KEY_MGMT_* used in key derivation */
+ u32 own_akm_suite_selector;
+ u32 peer_akm_suite_selector;
u8 pmkid[SAE_PMKID_LEN];
struct crypto_bignum *peer_commit_scalar;
struct crypto_bignum *peer_commit_scalar_accepted;
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 587cd88..da707e6 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -36,6 +36,9 @@
return pmk_len / 2;
case WPA_KEY_MGMT_OWE:
return pmk_len / 2;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ return pmk_len / 2;
default:
return 16;
}
@@ -72,6 +75,9 @@
return pmk_len <= 32 ? 16 : 32;
case WPA_KEY_MGMT_OWE:
return pmk_len <= 32 ? 16 : 32;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ return pmk_len <= 32 ? 16 : 32;
default:
return 16;
}
@@ -108,6 +114,9 @@
return pmk_len / 2;
case WPA_KEY_MGMT_OWE:
return pmk_len / 2;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ return pmk_len / 2;
default:
return 16;
}
@@ -143,7 +152,8 @@
akmp == WPA_KEY_MGMT_DPP ||
wpa_key_mgmt_ft(akmp) ||
wpa_key_mgmt_sha256(akmp) ||
- wpa_key_mgmt_sae(akmp) ||
+ (wpa_key_mgmt_sae(akmp) &&
+ !wpa_key_mgmt_sae_ext_key(akmp)) ||
wpa_key_mgmt_suite_b(akmp);
}
@@ -223,6 +233,32 @@
wpa_printf(MSG_DEBUG,
"WPA: EAPOL-Key MIC using AES-CMAC (AKM-defined - SAE)");
return omac1_aes_128(key, buf, len, mic);
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ wpa_printf(MSG_DEBUG,
+ "WPA: EAPOL-Key MIC using HMAC-SHA%u (AKM-defined - SAE-EXT-KEY)",
+ (unsigned int) key_len * 8 * 2);
+ if (key_len == 128 / 8) {
+ if (hmac_sha256(key, key_len, buf, len, hash))
+ return -1;
+#ifdef CONFIG_SHA384
+ } else if (key_len == 192 / 8) {
+ if (hmac_sha384(key, key_len, buf, len, hash))
+ return -1;
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ } else if (key_len == 256 / 8) {
+ if (hmac_sha512(key, key_len, buf, len, hash))
+ return -1;
+#endif /* CONFIG_SHA512 */
+ } else {
+ wpa_printf(MSG_INFO,
+ "SAE: Unsupported KCK length: %u",
+ (unsigned int) key_len);
+ return -1;
+ }
+ os_memcpy(mic, hash, key_len);
+ break;
#endif /* CONFIG_SAE */
#ifdef CONFIG_HS20
case WPA_KEY_MGMT_OSEN:
@@ -473,6 +509,36 @@
(unsigned int) pmk_len);
return -1;
#endif /* CONFIG_DPP */
+#ifdef CONFIG_SAE
+ } else if (wpa_key_mgmt_sae_ext_key(akmp)) {
+ if (pmk_len == 32) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: PTK derivation using PRF(SHA256)");
+ if (sha256_prf(pmk, pmk_len, label, data, data_len,
+ tmp, ptk_len) < 0)
+ return -1;
+#ifdef CONFIG_SHA384
+ } else if (pmk_len == 48) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: PTK derivation using PRF(SHA384)");
+ if (sha384_prf(pmk, pmk_len, label, data, data_len,
+ tmp, ptk_len) < 0)
+ return -1;
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ } else if (pmk_len == 64) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: PTK derivation using PRF(SHA512)");
+ if (sha512_prf(pmk, pmk_len, label, data, data_len,
+ tmp, ptk_len) < 0)
+ return -1;
+#endif /* CONFIG_SHA512 */
+ } else {
+ wpa_printf(MSG_INFO, "SAE: Unknown PMK length %u",
+ (unsigned int) pmk_len);
+ return -1;
+ }
+#endif /* CONFIG_SAE */
} else {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
@@ -1318,6 +1384,62 @@
/**
+ * wpa_ltf_keyseed - Compute LTF keyseed from KDK
+ * @ptk: Buffer that holds pairwise transient key
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher)
+{
+ u8 *buf;
+ size_t buf_len;
+ u8 hash[SHA384_MAC_LEN];
+ const u8 *kdk = ptk->kdk;
+ size_t kdk_len = ptk->kdk_len;
+ const char *label = "Secure LTF key seed";
+
+ if (!kdk || !kdk_len) {
+ wpa_printf(MSG_ERROR, "WPA: No KDK for LTF keyseed generation");
+ return -1;
+ }
+
+ buf = (u8 *)label;
+ buf_len = os_strlen(label);
+
+ if (pasn_use_sha384(akmp, cipher)) {
+ wpa_printf(MSG_DEBUG,
+ "WPA: Secure LTF keyseed using HMAC-SHA384");
+
+ if (hmac_sha384(kdk, kdk_len, buf, buf_len, hash)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: HMAC-SHA384 compute failed");
+ return -1;
+ }
+ os_memcpy(ptk->ltf_keyseed, hash, SHA384_MAC_LEN);
+ ptk->ltf_keyseed_len = SHA384_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ",
+ ptk->ltf_keyseed, ptk->ltf_keyseed_len);
+
+ } else {
+ wpa_printf(MSG_DEBUG, "WPA: LTF keyseed using HMAC-SHA256");
+
+ if (hmac_sha256(kdk, kdk_len, buf, buf_len, hash)) {
+ wpa_printf(MSG_ERROR,
+ "WPA: HMAC-SHA256 compute failed");
+ return -1;
+ }
+ os_memcpy(ptk->ltf_keyseed, hash, SHA256_MAC_LEN);
+ ptk->ltf_keyseed_len = SHA256_MAC_LEN;
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Secure LTF keyseed: ",
+ ptk->ltf_keyseed, ptk->ltf_keyseed_len);
+ }
+
+ return 0;
+}
+
+
+/**
* pasn_mic - Calculate PASN MIC
* @kck: The key confirmation key for the PASN PTKSA
* @akmp: Negotiated AKM
@@ -1479,8 +1601,12 @@
#ifdef CONFIG_SAE
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE)
return WPA_KEY_MGMT_SAE;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_SAE_EXT_KEY)
+ return WPA_KEY_MGMT_SAE_EXT_KEY;
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
return WPA_KEY_MGMT_FT_SAE;
+ if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY)
+ return WPA_KEY_MGMT_FT_SAE_EXT_KEY;
#endif /* CONFIG_SAE */
if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
@@ -2379,8 +2505,12 @@
return "WPS";
case WPA_KEY_MGMT_SAE:
return "SAE";
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ return "SAE-EXT-KEY";
case WPA_KEY_MGMT_FT_SAE:
return "FT-SAE";
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ return "FT-SAE-EXT-KEY";
case WPA_KEY_MGMT_OSEN:
return "OSEN";
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
@@ -2441,8 +2571,12 @@
return RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
if (akm & WPA_KEY_MGMT_SAE)
return RSN_AUTH_KEY_MGMT_SAE;
+ if (akm & WPA_KEY_MGMT_SAE_EXT_KEY)
+ return RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
if (akm & WPA_KEY_MGMT_FT_SAE)
return RSN_AUTH_KEY_MGMT_FT_SAE;
+ if (akm & WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ return RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
if (akm & WPA_KEY_MGMT_OWE)
return RSN_AUTH_KEY_MGMT_OWE;
if (akm & WPA_KEY_MGMT_DPP)
@@ -3030,6 +3164,9 @@
u32 selector;
const u8 *p;
size_t left;
+ u8 link_id;
+ char title[50];
+ int ret;
if (len == 0)
return 1;
@@ -3075,11 +3212,10 @@
return 0;
}
- if (left > 2 && selector == RSN_KEY_DATA_MAC_ADDR) {
+ if (left >= ETH_ALEN && selector == RSN_KEY_DATA_MAC_ADDR) {
ie->mac_addr = p;
- ie->mac_addr_len = left;
- wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
- pos, dlen);
+ wpa_printf(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key: " MACSTR,
+ MAC2STR(ie->mac_addr));
return 0;
}
@@ -3138,6 +3274,78 @@
return 0;
}
+ if (left >= RSN_MLO_GTK_KDE_PREFIX_LENGTH &&
+ selector == RSN_KEY_DATA_MLO_GTK) {
+ link_id = (p[0] & RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK) >>
+ RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT;
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ return 2;
+
+ ie->valid_mlo_gtks |= BIT(link_id);
+ ie->mlo_gtk[link_id] = p;
+ ie->mlo_gtk_len[link_id] = left;
+ ret = os_snprintf(title, sizeof(title),
+ "RSN: Link ID %u - MLO GTK KDE in EAPOL-Key",
+ link_id);
+ if (!os_snprintf_error(sizeof(title), ret))
+ wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+ return 0;
+ }
+
+ if (left >= RSN_MLO_IGTK_KDE_PREFIX_LENGTH &&
+ selector == RSN_KEY_DATA_MLO_IGTK) {
+ link_id = (p[8] & RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK) >>
+ RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT;
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ return 2;
+
+ ie->valid_mlo_igtks |= BIT(link_id);
+ ie->mlo_igtk[link_id] = p;
+ ie->mlo_igtk_len[link_id] = left;
+ ret = os_snprintf(title, sizeof(title),
+ "RSN: Link ID %u - MLO IGTK KDE in EAPOL-Key",
+ link_id);
+ if (!os_snprintf_error(sizeof(title), ret))
+ wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+ return 0;
+ }
+
+ if (left >= RSN_MLO_BIGTK_KDE_PREFIX_LENGTH &&
+ selector == RSN_KEY_DATA_MLO_BIGTK) {
+ link_id = (p[8] & RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK) >>
+ RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT;
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ return 2;
+
+ ie->valid_mlo_bigtks |= BIT(link_id);
+ ie->mlo_bigtk[link_id] = p;
+ ie->mlo_bigtk_len[link_id] = left;
+ ret = os_snprintf(title, sizeof(title),
+ "RSN: Link ID %u - MLO BIGTK KDE in EAPOL-Key",
+ link_id);
+ if (!os_snprintf_error(sizeof(title), ret))
+ wpa_hexdump_key(MSG_DEBUG, title, pos, dlen);
+ return 0;
+ }
+
+ if (left >= RSN_MLO_LINK_KDE_FIXED_LENGTH &&
+ selector == RSN_KEY_DATA_MLO_LINK) {
+ link_id = (p[0] & RSN_MLO_LINK_KDE_LI_LINK_ID_MASK) >>
+ RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT;
+ if (link_id >= MAX_NUM_MLO_LINKS)
+ return 2;
+
+ ie->valid_mlo_links |= BIT(link_id);
+ ie->mlo_link[link_id] = p;
+ ie->mlo_link_len[link_id] = left;
+ ret = os_snprintf(title, sizeof(title),
+ "RSN: Link ID %u - MLO Link KDE in EAPOL-Key",
+ link_id);
+ if (!os_snprintf_error(sizeof(title), ret))
+ wpa_hexdump(MSG_DEBUG, title, pos, dlen);
+ return 0;
+ }
+
return 2;
}
@@ -3373,6 +3581,9 @@
case WPA_KEY_MGMT_SAE:
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
break;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
+ break;
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
case WPA_KEY_MGMT_FILS_SHA256:
@@ -3586,6 +3797,7 @@
switch (data->key_mgmt) {
#ifdef CONFIG_SAE
case WPA_KEY_MGMT_SAE:
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
/* fall through */
#endif /* CONFIG_SAE */
#ifdef CONFIG_FILS
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index c28c55d..71b423c 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -81,8 +81,14 @@
#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
#define RSN_AUTH_KEY_MGMT_FT_FILS_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
#define RSN_AUTH_KEY_MGMT_OWE RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
-
+#define RSN_AUTH_KEY_MGMT_FT_PSK_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 20)
#define RSN_AUTH_KEY_MGMT_PASN RSN_SELECTOR(0x00, 0x0f, 0xac, 21)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384_UNRESTRICTED \
+ RSN_SELECTOR(0x00, 0x0f, 0xac, 22)
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA384 RSN_SELECTOR(0x00, 0x0f, 0xac, 23)
+#define RSN_AUTH_KEY_MGMT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 24)
+#define RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY RSN_SELECTOR(0x00, 0x0f, 0xac, 25)
#define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
@@ -126,6 +132,10 @@
#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define RSN_KEY_DATA_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 14)
+#define RSN_KEY_DATA_MLO_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 16)
+#define RSN_KEY_DATA_MLO_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 17)
+#define RSN_KEY_DATA_MLO_BIGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 18)
+#define RSN_KEY_DATA_MLO_LINK RSN_SELECTOR(0x00, 0x0f, 0xac, 19)
#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)
@@ -222,6 +232,7 @@
#define FILS_FT_MAX_LEN 48
#define WPA_PASN_KCK_LEN 32
#define WPA_PASN_MIC_MAX_LEN 24
+#define WPA_LTF_KEYSEED_MAX_LEN 48
/**
* struct wpa_ptk - WPA Pairwise Transient Key
@@ -234,12 +245,14 @@
u8 kck2[WPA_KCK_MAX_LEN]; /* FT reasoc Key Confirmation Key (KCK2) */
u8 kek2[WPA_KEK_MAX_LEN]; /* FT reassoc Key Encryption Key (KEK2) */
u8 kdk[WPA_KDK_MAX_LEN]; /* Key Derivation Key */
+ u8 ltf_keyseed[WPA_LTF_KEYSEED_MAX_LEN]; /* LTF Key seed */
size_t kck_len;
size_t kek_len;
size_t tk_len;
size_t kck2_len;
size_t kek2_len;
size_t kdk_len;
+ size_t ltf_keyseed_len;
int installed; /* 1 if key has already been installed to driver */
};
@@ -330,6 +343,40 @@
u8 bigtk[WPA_BIGTK_MAX_LEN];
} STRUCT_PACKED;
+#define RSN_MLO_GTK_KDE_PREFIX_LENGTH (1 + 6)
+#define RSN_MLO_GTK_KDE_PREFIX0_KEY_ID_MASK 0x03
+#define RSN_MLO_GTK_KDE_PREFIX0_TX 0x04
+#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_SHIFT 4
+#define RSN_MLO_GTK_KDE_PREFIX0_LINK_ID_MASK 0xF0
+
+#define RSN_MLO_IGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
+#define RSN_MLO_IGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
+struct rsn_mlo_igtk_kde {
+ u8 keyid[2];
+ u8 pn[6];
+ u8 prefix8;
+ u8 igtk[WPA_IGTK_MAX_LEN];
+} STRUCT_PACKED;
+
+#define RSN_MLO_BIGTK_KDE_PREFIX_LENGTH (2 + 6 + 1)
+#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_SHIFT 4
+#define RSN_MLO_BIGTK_KDE_PREFIX8_LINK_ID_MASK 0xF0
+struct rsn_mlo_bigtk_kde {
+ u8 keyid[2];
+ u8 pn[6];
+ u8 prefix8;
+ u8 bigtk[WPA_BIGTK_MAX_LEN];
+} STRUCT_PACKED;
+
+#define RSN_MLO_LINK_KDE_FIXED_LENGTH (1 + 6)
+#define RSN_MLO_LINK_KDE_LINK_INFO_INDEX 0
+#define RSN_MLO_LINK_KDE_LI_LINK_ID_SHIFT 0
+#define RSN_MLO_LINK_KDE_LI_LINK_ID_MASK 0x0F
+#define RSN_MLO_LINK_KDE_LI_RSNE_INFO 0x10
+#define RSN_MLO_LINK_KDE_LI_RSNXE_INFO 0x20
+#define RSN_MLO_LINK_KDE_LINK_MAC_INDEX 1
+
struct rsn_mdie {
u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
u8 ft_capab;
@@ -564,7 +611,6 @@
const u8 *gtk;
size_t gtk_len;
const u8 *mac_addr;
- size_t mac_addr_len;
const u8 *igtk;
size_t igtk_len;
const u8 *bigtk;
@@ -608,6 +654,19 @@
u16 aid;
const u8 *wmm;
size_t wmm_len;
+#define MAX_NUM_MLO_LINKS 15
+ u16 valid_mlo_gtks; /* bitmap of valid link GTK KDEs */
+ const u8 *mlo_gtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_gtk_len[MAX_NUM_MLO_LINKS];
+ u16 valid_mlo_igtks; /* bitmap of valid link IGTK KDEs */
+ const u8 *mlo_igtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_igtk_len[MAX_NUM_MLO_LINKS];
+ u16 valid_mlo_bigtks; /* bitmap of valid link BIGTK KDEs */
+ const u8 *mlo_bigtk[MAX_NUM_MLO_LINKS];
+ size_t mlo_bigtk_len[MAX_NUM_MLO_LINKS];
+ u16 valid_mlo_links; /* bitmap of valid MLO link KDEs */
+ const u8 *mlo_link[MAX_NUM_MLO_LINKS];
+ size_t mlo_link_len[MAX_NUM_MLO_LINKS];
};
int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie);
@@ -651,6 +710,8 @@
const u8 *data, size_t data_len,
const u8 *frame, size_t frame_len, u8 *mic);
+int wpa_ltf_keyseed(struct wpa_ptk *ptk, int akmp, int cipher);
+
int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
u8 *hash);
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
index 055bf73..ba54da5 100644
--- a/src/common/wpa_ctrl.h
+++ b/src/common/wpa_ctrl.h
@@ -92,6 +92,15 @@
#define WPA_EVENT_CHANNEL_SWITCH_STARTED "CTRL-EVENT-STARTED-CHANNEL-SWITCH "
/** Channel switch (followed by freq=<MHz> and other channel parameters) */
#define WPA_EVENT_CHANNEL_SWITCH "CTRL-EVENT-CHANNEL-SWITCH "
+/** MLO link channel switch started (followed by freq=<MHz> and other channel
+ * parameters)
+ */
+#define WPA_EVENT_LINK_CHANNEL_SWITCH_STARTED \
+ "CTRL-EVENT-STARTED-LINK-CHANNEL-SWITCH "
+/** MLO link channel switch (followed by freq=<MHz> and other channel
+ * parameters)
+ */
+#define WPA_EVENT_LINK_CHANNEL_SWITCH "CTRL-EVENT-LINK-CHANNEL-SWITCH "
/** SAE authentication failed due to unknown password identifier */
#define WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER \
"CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER "
@@ -213,6 +222,9 @@
#define DPP_EVENT_CSR "DPP-CSR "
#define DPP_EVENT_CHIRP_RX "DPP-CHIRP-RX "
#define DPP_EVENT_CONF_NEEDED "DPP-CONF-NEEDED "
+#define DPP_EVENT_PB_STATUS "DPP-PB-STATUS "
+#define DPP_EVENT_PB_RESULT "DPP-PB-RESULT "
+#define DPP_EVENT_RELAY_NEEDS_CONTROLLER "DPP-RELAY-NEEDS-CONTROLLER "
/* MESH events */
#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 67210a0..91df607 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -1011,6 +1011,16 @@
struct crypto_ec_key * crypto_ec_key_parse_priv(const u8 *der, size_t der_len);
/**
+ * crypto_ec_key_set_priv - Initialize EC key pair from raw key data
+ * @group: Identifying number for the ECC group
+ * @raw: Raw key data
+ * @raw_len: Length of @raw buffer
+ * Returns: EC key or %NULL on failure
+ */
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+ const u8 *raw, size_t raw_len);
+
+/**
* crypto_ec_key_parse_pub - Initialize EC key pair from SubjectPublicKeyInfo ASN.1
* @der: DER encoding of ASN.1 SubjectPublicKeyInfo
* @der_len: Length of @der buffer
@@ -1313,6 +1323,58 @@
*/
void crypto_rsa_key_free(struct crypto_rsa_key *key);
+enum hpke_mode {
+ HPKE_MODE_BASE = 0x00,
+ HPKE_MODE_PSK = 0x01,
+ HPKE_MODE_AUTH = 0x02,
+ HPKE_MODE_AUTH_PSK = 0x03,
+};
+
+enum hpke_kem_id {
+ HPKE_DHKEM_P256_HKDF_SHA256 = 0x0010,
+ HPKE_DHKEM_P384_HKDF_SHA384 = 0x0011,
+ HPKE_DHKEM_P521_HKDF_SHA512 = 0x0012,
+ HPKE_DHKEM_X5519_HKDF_SHA256 = 0x0020,
+ HPKE_DHKEM_X448_HKDF_SHA512 = 0x0021,
+};
+
+enum hpke_kdf_id {
+ HPKE_KDF_HKDF_SHA256 = 0x0001,
+ HPKE_KDF_HKDF_SHA384 = 0x0002,
+ HPKE_KDF_HKDF_SHA512 = 0x0003,
+};
+
+enum hpke_aead_id {
+ HPKE_AEAD_AES_128_GCM = 0x0001,
+ HPKE_AEAD_AES_256_GCM = 0x0002,
+ HPKE_AEAD_CHACHA20POLY1305 = 0x0003,
+};
+
+/**
+ * hpke_base_seal - HPKE base mode single-shot encrypt
+ * Returns: enc | ct; or %NULL on failure
+ */
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *peer_pub,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len);
+
+/**
+ * hpke_base_open - HPKE base mode single-shot decrypt
+ * @enc_ct: enc | ct
+ * Returns: pt; or %NULL on failure
+ */
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *own_priv,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *enc_ct, size_t enc_ct_len);
+
/**
* crypto_unload - Unload crypto resources
*
diff --git a/src/crypto/crypto_module_tests.c b/src/crypto/crypto_module_tests.c
index fafb688..4147f41 100644
--- a/src/crypto/crypto_module_tests.c
+++ b/src/crypto/crypto_module_tests.c
@@ -2190,6 +2190,285 @@
}
+#ifdef CONFIG_DPP3
+
+static const struct hpke_test {
+ const char *name;
+ enum hpke_mode mode;
+ enum hpke_kem_id kem_id;
+ enum hpke_kdf_id kdf_id;
+ enum hpke_aead_id aead_id;
+ const char *info;
+ int sk_r_group;
+ const char *pk_r;
+ const char *sk_r;
+ const char *enc;
+ const char *pt;
+ const char *aad;
+ const char *ct;
+} hpke_tests[] = {
+ {
+ .name = "A.3. DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+ .kdf_id = HPKE_KDF_HKDF_SHA256,
+ .aead_id = HPKE_AEAD_AES_128_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 19,
+ .pk_r = "04fe8c19ce0905191ebc298a9245792531f26f0cece2460639e8bc39cb7f706a826a779b4cf969b8a0e539c7f62fb3d30ad6aa8f80e30f1d128aafd68a2ce72ea0",
+ .sk_r = "f3ce7fdae57e1a310d87f1ebbde6f328be0a99cdbcadf4d6589cf29de4b8ffd2",
+ .enc = "04a92719c6195d5085104f469a8b9814d5838ff72b60501e2c4466e5e67b325ac98536d7b61a1af4b78e5b7f951c0900be863c403ce65c9bfcb9382657222d18c4",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "5ad590bb8baa577f8619db35a36311226a896e7342a6d836d8b7bcd2f20b6c7f9076ac232e3ab2523f39513434",
+ },
+ {
+ .name = "A.4. DHKEM(P-256, HKDF-SHA256), HKDF-SHA512, AES-128-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+ .kdf_id = HPKE_KDF_HKDF_SHA512,
+ .aead_id = HPKE_AEAD_AES_128_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 19,
+ .pk_r = "04085aa5b665dc3826f9650ccbcc471be268c8ada866422f739e2d531d4a8818a9466bc6b449357096232919ec4fe9070ccbac4aac30f4a1a53efcf7af90610edd",
+ .sk_r = "3ac8530ad1b01885960fab38cf3cdc4f7aef121eaa239f222623614b4079fb38",
+ .enc = "0493ed86735bdfb978cc055c98b45695ad7ce61ce748f4dd63c525a3b8d53a15565c6897888070070c1579db1f86aaa56deb8297e64db7e8924e72866f9a472580",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "d3cf4984931484a080f74c1bb2a6782700dc1fef9abe8442e44a6f09044c88907200b332003543754eb51917ba",
+ },
+ {
+ .name = "A.6. DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+ .kdf_id = HPKE_KDF_HKDF_SHA512,
+ .aead_id = HPKE_AEAD_AES_256_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 21,
+ .pk_r = "0401b45498c1714e2dce167d3caf162e45e0642afc7ed435df7902ccae0e84ba0f7d373f646b7738bbbdca11ed91bdeae3cdcba3301f2457be452f271fa6837580e661012af49583a62e48d44bed350c7118c0d8dc861c238c72a2bda17f64704f464b57338e7f40b60959480c0e58e6559b190d81663ed816e523b6b6a418f66d2451ec64",
+ .sk_r = "01462680369ae375e4b3791070a7458ed527842f6a98a79ff5e0d4cbde83c27196a3916956655523a6a2556a7af62c5cadabe2ef9da3760bb21e005202f7b2462847",
+ .enc = "040138b385ca16bb0d5fa0c0665fbbd7e69e3ee29f63991d3e9b5fa740aab8900aaeed46ed73a49055758425a0ce36507c54b29cc5b85a5cee6bae0cf1c21f2731ece2013dc3fb7c8d21654bb161b463962ca19e8c654ff24c94dd2898de12051f1ed0692237fb02b2f8d1dc1c73e9b366b529eb436e98a996ee522aef863dd5739d2f29b0",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "170f8beddfe949b75ef9c387e201baf4132fa7374593dfafa90768788b7b2b200aafcc6d80ea4c795a7c5b841a",
+ },
+ { /* self-generated test vector for P-384 */
+ .name = "custom DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+ .kdf_id = HPKE_KDF_HKDF_SHA384,
+ .aead_id = HPKE_AEAD_AES_256_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 20,
+ .pk_r = "049c0e4dcbbb3c80715cafaa1839d0bc3c3adcc95eb8062f84175f9c3cec115e6b799061c65a0605907785c25b3571564706a8ba6a204452b38c7c205db17d328f2353df05d5f1c568e7503331178c36c2d37bbed48401295407face3f8dae5ed8",
+ .sk_r = "cabffb07d20ffcfdaa043e1de49e1654659e0f0aba5de56523e8b73dc80c579a9e5c89ed3810ec21c4bafcf74ad2a245",
+ .enc = "04b30bea96d0e51582033b02a4d676d0464a5eb2d858be86cda1c4e6f8b2aa9fb80f5365483f781b1b3a8b3b8efd50b0f7bca16f06d0435fa3da1d671ea0a318b40fe170a074923c651e5dc824966b7b98d0e36bdf932875dae7130369a793cecc",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "ae7feccfea0f8fcd620d15369a28db8701cdc90d55c20efff6296bd441697b0da34671d1f3c4864183e86d27fc",
+ },
+ { /* self-generated test vector for BP-256 */
+ .name = "custom PB-256 using DHKEM(P-256, HKDF-SHA256), HKDF-SHA256, AES-128-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P256_HKDF_SHA256,
+ .kdf_id = HPKE_KDF_HKDF_SHA256,
+ .aead_id = HPKE_AEAD_AES_128_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 28,
+ .pk_r = "04a2cb9c4cae90cdc1c27516e9f84b6b166e4b1dcc517286268239ddb0bf74cca6390fed092ac4423ab2192b8bb41a4824d908d2053b93fc813830bebac5ce19b9",
+ .sk_r = "11d9db41c4341166ca52f5a1775595c0bdb4934350daeb7bce659c4b7a40e314",
+ .enc = "047a25e309c7ee50ec27f13d44734a3ccd8c703e3affcc728513df416511ef9bf02f5e7750e7415de8b5f306ebd3fc88ea9b9368523eb1733a8d82c1a877e5a0f4",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "17c84b3f07f6ffe08ff2be45c709ea782229504aa5b2253876725c6c39f8d8c992304fc5877994f79d6c10d462",
+ },
+ { /* self-generated test vector for BP-384 */
+ .name = "custom PB-384 using DHKEM(P-384, HKDF-SHA384), HKDF-SHA384, AES-256-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P384_HKDF_SHA384,
+ .kdf_id = HPKE_KDF_HKDF_SHA384,
+ .aead_id = HPKE_AEAD_AES_256_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 29,
+ .pk_r = "041f4199ad28835908079c45d165d55630098be53eb4beede9921f5b2204fa396111f99ac54c56411f7cb2c43ec18d8e604d895027228cf975f5a4b598f189d8fb03e3fefe020258c40d4d1b15fd7587d209925d67a41f9659a8ed6f662fb441e4",
+ .sk_r = "7017cf8a5a9a81ad4e0d755ccbea27a378b787561f8d5662639850805fefcbaab6b9a15729872abb7dc53d19a6cf77e4",
+ .enc = "0415d49dedc5bc1ffe9f8de9022c266bb605ec6cd7b77b6ce68974095398856f8aefa4b7abbfbd496b99a2dda3a9c65f1a71b9d40255aa1c7c4205a8b4ef611b96ed29fd2d7b0cde4c0e82058805e6276025cc4fc606f6e5771c31bd9704e9ba0b",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "5f5e9f82bedadec0e9b01a1b304cb48b05c0d6d397b1c8a95ed541218ec54f634a41cbc4066910a409e47b254e",
+ },
+ { /* self-generated test vector for BP-512 */
+ .name = "custom PB-512 using DHKEM(P-521, HKDF-SHA512), HKDF-SHA512, AES-256-GCM",
+ .mode = HPKE_MODE_BASE,
+ .kem_id = HPKE_DHKEM_P521_HKDF_SHA512,
+ .kdf_id = HPKE_KDF_HKDF_SHA512,
+ .aead_id = HPKE_AEAD_AES_256_GCM,
+ .info = "4f6465206f6e2061204772656369616e2055726e",
+ .sk_r_group = 30,
+ .pk_r = "049e81046a531365a3b5215ac37e7b38f5fa34f86c4eb2e03113b197390a26c555bb007596e131c2541f336eb24a45f44283b5b53fedddfa5642675602fdec17d34120a35efffb44952e32dee7732f2f3245c3314269996b610703a63fb8555a75ca5092690a1125ae8712c1e31fd77aee42bd052e71f9f9459814d6f4065bcea0",
+ .sk_r = "483b6882608182b296843fa7dfffbdd61ed0372574d4aa32a035c8e33a493927aaf00d42bd9124ebe4df26010b38124668c02b35a749e74845d565734310cfe9",
+ .enc = "04158d18473aeb3b283d3345b1a87d3de2b192ff9e41b5a98f91daacfb24be72e698cbc04c33078681e507bf346c0ea70c927083a22ca9ea027f420067ee42285b798d95fea51002d097ce28371883202bfd300fb64943669e32c6f1a348087368bb480b757892ebd199a9389978c92cbc44076626d705a771fbbd90c030a6767e",
+ .pt = "4265617574792069732074727574682c20747275746820626561757479",
+ .aad = "436f756e742d30",
+ .ct = "033d91c4514857da5b833635180c1acc09f175cbf44777a7b71e177705cfd17437b1c85d671dd767bb4fe20e2e",
+ },
+};
+
+
+static int run_hpke_test(const struct hpke_test *test)
+{
+ struct wpabuf *info, *pk_r, *sk_r, *enc, *pt, *aad, *ct;
+ struct wpabuf *res_pt = NULL, *enc_ct = NULL, *res_ct = NULL;
+ struct crypto_ec_key *own_priv = NULL, *peer_pub = NULL;
+ int res = -1;
+ size_t coord_len;
+
+ wpa_printf(MSG_INFO, "- %s", test->name);
+
+ info = wpabuf_parse_bin(test->info);
+ pk_r = wpabuf_parse_bin(test->pk_r);
+ sk_r = wpabuf_parse_bin(test->sk_r);
+ enc = wpabuf_parse_bin(test->enc);
+ pt = wpabuf_parse_bin(test->pt);
+ aad = wpabuf_parse_bin(test->aad);
+ ct = wpabuf_parse_bin(test->ct);
+ if (!info || !pk_r || !sk_r || !enc || !pt || !aad || !ct) {
+ wpa_printf(MSG_ERROR, "Could not parse test data");
+ goto fail;
+ }
+
+ /* Receiver - decryption against the test vector */
+
+ enc_ct = wpabuf_concat(enc, ct);
+ enc = NULL;
+ ct = NULL;
+ if (!enc_ct)
+ goto fail;
+
+ own_priv = crypto_ec_key_set_priv(test->sk_r_group, wpabuf_head(sk_r),
+ wpabuf_len(sk_r));
+ if (!own_priv) {
+ wpa_printf(MSG_ERROR,
+ "HPKE base open - failed to set private key");
+ goto fail;
+ }
+
+ res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+ own_priv,
+ wpabuf_head(info), wpabuf_len(info),
+ wpabuf_head(aad), wpabuf_len(aad),
+ wpabuf_head(enc_ct), wpabuf_len(enc_ct));
+ if (!res_pt) {
+ wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt");
+ wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+ goto fail;
+ }
+ if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+ os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+ wpabuf_len(pt)) != 0) {
+ wpa_printf(MSG_ERROR,
+ "HPKE base open - failed - decryption mismatch");
+ goto fail;
+ }
+
+ /* Sender - encryption (randomized algorithm) */
+
+ if (test->sk_r_group == 19)
+ coord_len = 32;
+ else if (test->sk_r_group == 20)
+ coord_len = 48;
+ else if (test->sk_r_group == 21)
+ coord_len = 66;
+ else if (test->sk_r_group == 28)
+ coord_len = 32;
+ else if (test->sk_r_group == 29)
+ coord_len = 48;
+ else if (test->sk_r_group == 30)
+ coord_len = 64;
+ else
+ goto fail;
+ if (wpabuf_len(pk_r) != 1 + 2 * coord_len) {
+ wpa_printf(MSG_ERROR, "Unexpected pkR length (%zu != %zu)",
+ wpabuf_len(pk_r), 1 + 2 * coord_len);
+ goto fail;
+ }
+ peer_pub = crypto_ec_key_set_pub(test->sk_r_group,
+ wpabuf_head_u8(pk_r) + 1,
+ wpabuf_head_u8(pk_r) + 1 + coord_len,
+ coord_len);
+ if (!peer_pub) {
+ wpa_printf(MSG_ERROR,
+ "HPKE base open - failed to set public key");
+ goto fail;
+ }
+
+ res_ct = hpke_base_seal(test->kem_id, test->kdf_id, test->aead_id,
+ peer_pub,
+ wpabuf_head(info), wpabuf_len(info),
+ wpabuf_head(aad), wpabuf_len(aad),
+ wpabuf_head(pt), wpabuf_len(pt));
+ if (!res_ct) {
+ wpa_printf(MSG_ERROR, "HPKE base open - failed to encrypt");
+ goto fail;
+ }
+
+ /* Receiver - decryption (to verify own encryption) */
+
+ wpabuf_free(res_pt);
+ res_pt = hpke_base_open(test->kem_id, test->kdf_id, test->aead_id,
+ own_priv,
+ wpabuf_head(info), wpabuf_len(info),
+ wpabuf_head(aad), wpabuf_len(aad),
+ wpabuf_head(res_ct), wpabuf_len(res_ct));
+ if (!res_pt) {
+ wpa_printf(MSG_ERROR, "HPKE base open - failed to decrypt own encrypted version");
+ goto fail;
+ }
+ if (wpabuf_len(res_pt) != wpabuf_len(pt) ||
+ os_memcmp(wpabuf_head(res_pt), wpabuf_head(pt),
+ wpabuf_len(pt)) != 0) {
+ wpa_printf(MSG_ERROR,
+ "HPKE base open - failed - decryption mismatch for own encrypted version");
+ wpa_hexdump_buf(MSG_INFO, "pt", res_pt);
+ goto fail;
+ }
+
+ res = 0;
+fail:
+ wpabuf_free(info);
+ wpabuf_free(pk_r);
+ wpabuf_free(sk_r);
+ wpabuf_free(enc);
+ wpabuf_free(pt);
+ wpabuf_free(aad);
+ wpabuf_free(ct);
+ wpabuf_free(enc_ct);
+ wpabuf_free(res_pt);
+ wpabuf_free(res_ct);
+ crypto_ec_key_deinit(own_priv);
+ return res;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
+static int test_hpke(void)
+{
+#ifdef CONFIG_DPP3
+ unsigned int i;
+
+ wpa_printf(MSG_INFO, "RFC 9180 - HPKE");
+ for (i = 0; i < ARRAY_SIZE(hpke_tests); i++) {
+ if (run_hpke_test(&hpke_tests[i]) < 0)
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "HPKE base open test cases passed");
+#endif /* CONFIG_DPP3 */
+ return 0;
+}
+
+
static int test_ms_funcs(void)
{
#ifndef CONFIG_FIPS
@@ -2310,6 +2589,7 @@
test_sha384() ||
test_fips186_2_prf() ||
test_extract_expand_hkdf() ||
+ test_hpke() ||
test_ms_funcs())
ret = -1;
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index 08e9f26..f058e06 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -16,6 +16,7 @@
#include <openssl/dh.h>
#include <openssl/hmac.h>
#include <openssl/rand.h>
+#include <openssl/rsa.h>
#include <openssl/pem.h>
#ifdef CONFIG_ECC
#include <openssl/ec.h>
@@ -25,7 +26,6 @@
#include <openssl/provider.h>
#include <openssl/core_names.h>
#include <openssl/param_build.h>
-#include <openssl/rsa.h>
#include <openssl/encoder.h>
#include <openssl/decoder.h>
#else /* OpenSSL version >= 3.0 */
@@ -3039,6 +3039,141 @@
}
+struct crypto_ec_key * crypto_ec_key_set_priv(int group,
+ const u8 *raw, size_t raw_len)
+{
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ const char *group_name;
+ OSSL_PARAM params[4];
+ EVP_PKEY_CTX *ctx = NULL;
+ EVP_PKEY *pkey = NULL;
+ BIGNUM *priv;
+ EC_POINT *pub = NULL;
+ EC_GROUP *ec_group = NULL;
+ size_t len;
+ u8 *pub_bin = NULL;
+ u8 *priv_bin = NULL;
+ int priv_bin_len;
+
+ group_name = crypto_ec_group_2_name(group);
+ if (!group_name)
+ return NULL;
+
+ priv = BN_bin2bn(raw, raw_len, NULL);
+ if (!priv)
+ return NULL;
+ priv_bin = os_malloc(raw_len);
+ if (!priv_bin)
+ goto fail;
+ priv_bin_len = BN_bn2lebinpad(priv, priv_bin, raw_len);
+ if (priv_bin_len < 0)
+ goto fail;
+
+ ec_group = EC_GROUP_new_by_curve_name(crypto_ec_group_2_nid(group));
+ if (!ec_group)
+ goto fail;
+ pub = EC_POINT_new(ec_group);
+ if (!pub ||
+ EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1)
+ goto fail;
+ len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+ NULL, 0, NULL);
+ if (len == 0)
+ goto fail;
+ pub_bin = os_malloc(len);
+ if (!pub_bin)
+ goto fail;
+ len = EC_POINT_point2oct(ec_group, pub, POINT_CONVERSION_UNCOMPRESSED,
+ pub_bin, len, NULL);
+ if (len == 0)
+ goto fail;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
+ (char *) group_name, 0);
+ params[1] = OSSL_PARAM_construct_BN(OSSL_PKEY_PARAM_PRIV_KEY,
+ priv_bin, priv_bin_len);
+ params[2] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
+ pub_bin, len);
+ params[3] = OSSL_PARAM_construct_end();
+
+ ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
+ if (!ctx ||
+ EVP_PKEY_fromdata_init(ctx) <= 0 ||
+ EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEYPAIR, params) <= 0)
+ goto fail;
+
+out:
+ bin_clear_free(priv_bin, raw_len);
+ os_free(pub_bin);
+ BN_clear_free(priv);
+ EVP_PKEY_CTX_free(ctx);
+ EC_POINT_free(pub);
+ EC_GROUP_free(ec_group);
+ return (struct crypto_ec_key *) pkey;
+
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+#else /* OpenSSL version >= 3.0 */
+ EC_KEY *eckey = NULL;
+ EVP_PKEY *pkey = NULL;
+ BIGNUM *priv = NULL;
+ int nid;
+ const EC_GROUP *ec_group;
+ EC_POINT *pub = NULL;
+
+ nid = crypto_ec_group_2_nid(group);
+ if (nid < 0) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Unsupported group %d", group);
+ return NULL;
+ }
+
+ eckey = EC_KEY_new_by_curve_name(nid);
+ priv = BN_bin2bn(raw, raw_len, NULL);
+ if (!eckey || !priv ||
+ EC_KEY_set_private_key(eckey, priv) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ ec_group = EC_KEY_get0_group(eckey);
+ if (!ec_group)
+ goto fail;
+ pub = EC_POINT_new(ec_group);
+ if (!pub ||
+ EC_POINT_mul(ec_group, pub, priv, NULL, NULL, NULL) != 1 ||
+ EC_KEY_set_public_key(eckey, pub) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to set EC_KEY(pub): %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_assign_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "OpenSSL: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_clear_free(priv);
+ EC_POINT_free(pub);
+ return (struct crypto_ec_key *) pkey;
+
+fail:
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
struct crypto_ec_key * crypto_ec_key_parse_pub(const u8 *der, size_t der_len)
{
EVP_PKEY *pkey;
@@ -3212,7 +3347,6 @@
EC_KEY *eckey;
EVP_PKEY *pkey = NULL;
- wpa_printf(MSG_INFO, "JKM:%s", __func__);
eckey = EC_KEY_new();
if (!eckey ||
EC_KEY_set_group(eckey, ec->group) != 1 ||
@@ -4432,6 +4566,769 @@
}
+#ifdef CONFIG_DPP3
+
+#define HPKE_MAX_SHARED_SECRET_LEN 66
+#define HPKE_MAX_HASH_LEN 64
+#define HPKE_MAX_KEY_LEN 32
+#define HPKE_MAX_NONCE_LEN 12
+#define HPKE_MAX_PUB_LEN (1 + 2 * 66)
+
+struct hpke_context {
+ /* KEM */
+ enum hpke_kem_id kem_id;
+ int kem_nid;
+ int iana_group;
+ size_t n_pk;
+ size_t n_secret;
+ const EVP_MD *kem_h;
+ size_t kem_n_h;
+
+ /* KDF */
+ enum hpke_kdf_id kdf_id;
+ const EVP_MD *kdf_h;
+ size_t n_h;
+
+ /* AEAD */
+ enum hpke_aead_id aead_id;
+ const EVP_CIPHER *cipher;
+ size_t n_k;
+ size_t n_n;
+ size_t n_t;
+ u8 key[HPKE_MAX_KEY_LEN];
+ u8 base_nonce[HPKE_MAX_NONCE_LEN];
+};
+
+
+static void hpke_free_context(struct hpke_context *ctx)
+{
+ bin_clear_free(ctx, sizeof(*ctx));
+}
+
+
+static struct hpke_context * hpke_get_context(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *key)
+{
+ struct hpke_context *ctx;
+ int group;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (!ctx)
+ return NULL;
+
+ ctx->kem_id = kem_id;
+ switch (kem_id) {
+ case HPKE_DHKEM_P256_HKDF_SHA256:
+ ctx->kem_nid = NID_X9_62_prime256v1;
+ ctx->iana_group = 19;
+ ctx->n_pk = 65;
+ ctx->n_secret = 32;
+ ctx->kem_h = EVP_sha256();
+ ctx->kem_n_h = 32;
+ break;
+ case HPKE_DHKEM_P384_HKDF_SHA384:
+ ctx->kem_nid = NID_secp384r1;
+ ctx->iana_group = 20;
+ ctx->n_pk = 97;
+ ctx->n_secret = 48;
+ ctx->kem_h = EVP_sha384();
+ ctx->kem_n_h = 48;
+ break;
+ case HPKE_DHKEM_P521_HKDF_SHA512:
+ ctx->kem_nid = NID_secp521r1;
+ ctx->iana_group = 21;
+ ctx->n_pk = 133;
+ ctx->n_secret = 64;
+ ctx->kem_h = EVP_sha512();
+ ctx->kem_n_h = 64;
+ break;
+ default:
+ goto fail;
+ }
+
+ ctx->kdf_id = kdf_id;
+ switch (kdf_id) {
+ case HPKE_KDF_HKDF_SHA256:
+ ctx->kdf_h = EVP_sha256();
+ ctx->n_h = 32;
+ break;
+ case HPKE_KDF_HKDF_SHA384:
+ ctx->kdf_h = EVP_sha384();
+ ctx->n_h = 48;
+ break;
+ case HPKE_KDF_HKDF_SHA512:
+ ctx->kdf_h = EVP_sha512();
+ ctx->n_h = 64;
+ break;
+ default:
+ goto fail;
+ }
+
+ ctx->aead_id = aead_id;
+ switch (aead_id) {
+ case HPKE_AEAD_AES_128_GCM:
+ ctx->cipher = EVP_aes_128_gcm();
+ ctx->n_k = 16;
+ ctx->n_n = 12;
+ ctx->n_t = 16;
+ break;
+ case HPKE_AEAD_AES_256_GCM:
+ ctx->cipher = EVP_aes_256_gcm();
+ ctx->n_k = 32;
+ ctx->n_n = 12;
+ ctx->n_t = 16;
+ break;
+ default:
+ goto fail;
+ }
+
+ /* Convert BP-256/384/512 to P-256/384/521 for DPP */
+ group = crypto_ec_key_group(key);
+ if (group == 28 && ctx->iana_group == 19) {
+ ctx->iana_group = 28;
+ } else if (group == 29 && ctx->iana_group == 20) {
+ ctx->iana_group = 29;
+ } else if (group == 30 && ctx->iana_group == 21) {
+ ctx->iana_group = 30;
+ ctx->n_pk = 129;
+ }
+ if (group != ctx->iana_group) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:group mismatch (%d != %d)",
+ __func__, group, ctx->iana_group);
+ goto fail;
+ }
+
+ return ctx;
+fail:
+ hpke_free_context(ctx);
+ return NULL;
+}
+
+
+static size_t hpke_suite_id(struct hpke_context *ctx, bool kem, u8 *suite_id)
+{
+ size_t suite_id_len;
+
+ if (kem) {
+ os_memcpy(suite_id, "KEM", 3);
+ WPA_PUT_BE16(&suite_id[3], ctx->kem_id);
+ suite_id_len = 5;
+ } else {
+ os_memcpy(suite_id, "HPKE", 4);
+ WPA_PUT_BE16(&suite_id[4], ctx->kem_id);
+ WPA_PUT_BE16(&suite_id[6], ctx->kdf_id);
+ WPA_PUT_BE16(&suite_id[8], ctx->aead_id);
+ suite_id_len = 10;
+ }
+ return suite_id_len;
+}
+
+
+static int hpke_labeled_extract(struct hpke_context *ctx, bool kem,
+ const u8 *salt, size_t salt_len,
+ const char *label,
+ const u8 *ikm, size_t ikm_len, u8 *prk)
+{
+ u8 zero[HPKE_MAX_HASH_LEN];
+ u8 suite_id[10];
+ size_t suite_id_len;
+ unsigned int mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *hmac;
+ OSSL_PARAM params[2];
+ EVP_MAC_CTX *hctx;
+ size_t mlen;
+ int res;
+#else /* OpenSSL version >= 3.0 */
+ HMAC_CTX *hctx;
+ int res;
+#endif /* OpenSSL version >= 3.0 */
+
+ if (!salt || !salt_len) {
+ salt_len = mdlen;
+ os_memset(zero, 0, salt_len);
+ salt = zero;
+ }
+
+ suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+
+ /* labeled_ikm = concat("HPKE-v1", suite_id, label, ikm)
+ * return Extract(salt, labeled_ikm) */
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ if (!hmac)
+ return -1;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(
+ "digest",
+ (char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+ params[1] = OSSL_PARAM_construct_end();
+
+ hctx = EVP_MAC_CTX_new(hmac);
+ EVP_MAC_free(hmac);
+ if (!hctx)
+ return -1;
+
+ if (EVP_MAC_init(hctx, salt, salt_len, params) != 1)
+ goto fail;
+
+ if (EVP_MAC_update(hctx, (const unsigned char *) "HPKE-v1", 7) != 1 ||
+ EVP_MAC_update(hctx, suite_id, suite_id_len) != 1 ||
+ EVP_MAC_update(hctx, (const unsigned char *) label,
+ os_strlen(label)) != 1 ||
+ EVP_MAC_update(hctx, ikm, ikm_len) != 1)
+ goto fail;
+
+ res = EVP_MAC_final(hctx, prk, &mlen, mdlen);
+ EVP_MAC_CTX_free(hctx);
+
+ return res == 1 ? 0 : -1;
+fail:
+ EVP_MAC_CTX_free(hctx);
+ return -1;
+#else /* OpenSSL version >= 3.0 */
+ hctx = HMAC_CTX_new();
+ if (!hctx)
+ return -1;
+ res = HMAC_Init_ex(hctx, salt, salt_len, kem ? ctx->kem_h : ctx->kdf_h,
+ NULL);
+ if (res != 1)
+ goto done;
+
+ HMAC_Update(hctx, (const unsigned char *) "HPKE-v1", 7);
+ HMAC_Update(hctx, suite_id, suite_id_len);
+ HMAC_Update(hctx, (const unsigned char *) label, os_strlen(label));
+ HMAC_Update(hctx, ikm, ikm_len);
+
+ res = HMAC_Final(hctx, prk, &mdlen);
+done:
+ HMAC_CTX_free(hctx);
+
+ return res == 1 ? 0 : -1;
+#endif /* OpenSSL version >= 3.0 */
+}
+
+
+static int
+hpke_labeled_expand(struct hpke_context *ctx, bool kem, const u8 *prk,
+ const char *label, const u8 *info, size_t info_len,
+ u8 *out, size_t out_len)
+{
+ u8 suite_id[10];
+ size_t suite_id_len;
+ u8 hash[HPKE_MAX_HASH_LEN];
+ u8 iter = 0;
+ size_t label_len = os_strlen(label);
+ u8 *pos;
+ size_t left = out_len, clen;
+ int res = -1;
+ u8 *labeled_info;
+ size_t labeled_info_len;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC *hmac;
+ OSSL_PARAM params[2];
+ EVP_MAC_CTX *hctx = NULL;
+ size_t mdlen;
+#else /* OpenSSL version >= 3.0 */
+ HMAC_CTX *hctx;
+ unsigned int mdlen;
+#endif /* OpenSSL version >= 3.0 */
+
+ /* labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id,
+ * label, info)
+ * return Expand(prk, labeled_info, L) */
+ suite_id_len = hpke_suite_id(ctx, kem, suite_id);
+ labeled_info_len = 2 + 7 + suite_id_len + label_len + info_len;
+ labeled_info = os_malloc(labeled_info_len);
+ if (!labeled_info)
+ return -1;
+ pos = labeled_info;
+ WPA_PUT_BE16(pos, out_len);
+ pos += 2;
+ os_memcpy(pos, "HPKE-v1", 7);
+ pos += 7;
+ os_memcpy(pos, suite_id, suite_id_len);
+ pos += suite_id_len;
+ os_memcpy(pos, label, label_len);
+ pos += label_len;
+ if (info && info_len) {
+ os_memcpy(pos, info, info_len);
+ pos += info_len;
+ }
+
+ pos = out;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ hmac = EVP_MAC_fetch(NULL, "HMAC", NULL);
+ if (!hmac)
+ return -1;
+
+ params[0] = OSSL_PARAM_construct_utf8_string(
+ "digest",
+ (char *) EVP_MD_get0_name(kem ? ctx->kem_h : ctx->kdf_h), 0);
+ params[1] = OSSL_PARAM_construct_end();
+#else /* OpenSSL version >= 3.0 */
+ hctx = HMAC_CTX_new();
+ if (!hctx)
+ return -1;
+#endif /* OpenSSL version >= 3.0 */
+
+ while (left > 0) {
+ mdlen = kem ? ctx->kem_n_h : ctx->n_h;
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_CTX_free(hctx);
+ hctx = EVP_MAC_CTX_new(hmac);
+ if (!hctx)
+ return -1;
+
+ if (EVP_MAC_init(hctx, prk, mdlen, params) != 1)
+ goto fail;
+
+ if (iter > 0 && EVP_MAC_update(hctx, hash, mdlen) != 1)
+ goto fail;
+ if (iter == 255)
+ goto fail;
+ iter++;
+
+ if (EVP_MAC_update(hctx, labeled_info, labeled_info_len) != 1 ||
+ EVP_MAC_update(hctx, &iter, sizeof(iter)) != 1)
+ goto fail;
+
+ if (EVP_MAC_final(hctx, hash, &mdlen, mdlen) != 1)
+ goto fail;
+#else /* OpenSSL version >= 3.0 */
+ if (HMAC_Init_ex(hctx, prk, mdlen,
+ kem ? ctx->kem_h : ctx->kdf_h,
+ NULL) != 1)
+ goto fail;
+
+ if (iter > 0)
+ HMAC_Update(hctx, hash, mdlen);
+ if (iter == 255)
+ goto fail;
+ iter++;
+ HMAC_Update(hctx, labeled_info, labeled_info_len);
+ HMAC_Update(hctx, &iter, sizeof(iter));
+
+ if (HMAC_Final(hctx, hash, &mdlen) != 1)
+ goto fail;
+ HMAC_CTX_reset(hctx);
+#endif /* OpenSSL version >= 3.0 */
+
+ clen = left > mdlen ? mdlen : left;
+ os_memcpy(pos, hash, clen);
+ pos += clen;
+ left -= clen;
+ }
+ res = 0;
+fail:
+#if OPENSSL_VERSION_NUMBER >= 0x30000000L
+ EVP_MAC_free(hmac);
+ EVP_MAC_CTX_free(hctx);
+#else /* OpenSSL version >= 3.0 */
+ HMAC_CTX_free(hctx);
+#endif /* OpenSSL version >= 3.0 */
+ os_free(labeled_info);
+
+ return res;
+}
+
+
+static int hpke_extract_and_expand(struct hpke_context *ctx,
+ const u8 *dhss, size_t dhss_len,
+ const u8 *enc, size_t enc_len,
+ const u8 *pk_rm, size_t pk_rm_len,
+ u8 *shared_secret)
+{
+ u8 kem_context[2 * HPKE_MAX_PUB_LEN];
+ u8 eae_prk[HPKE_MAX_HASH_LEN];
+
+ /* eae_prk = LabeledExtract("", "eae_prk", dh) */
+ if (hpke_labeled_extract(ctx, true, NULL, 0, "eae_prk", dhss, dhss_len,
+ eae_prk) < 0)
+ return -1;
+
+ if (enc_len > HPKE_MAX_PUB_LEN || pk_rm_len > HPKE_MAX_PUB_LEN)
+ return -1;
+ /* kem_context = concat(enc, pkRm) */
+ os_memcpy(kem_context, enc, enc_len);
+ os_memcpy(&kem_context[enc_len], pk_rm, pk_rm_len);
+
+ /* shared_secret = LabeledExpand(eae_prk, "shared_secret",
+ * kem_context, Nsecret) */
+ if (hpke_labeled_expand(ctx, true, eae_prk, "shared_secret",
+ kem_context, enc_len + pk_rm_len,
+ shared_secret, ctx->n_secret) < 0)
+ return -1;
+
+ forced_memzero(eae_prk, sizeof(eae_prk));
+ return 0;
+}
+
+
+static int hpke_key_schedule(struct hpke_context *ctx, const u8 *shared_secret,
+ const u8 *info, size_t info_len)
+{
+ u8 key_schedule_context[1 + 2 * HPKE_MAX_HASH_LEN];
+ u8 secret[HPKE_MAX_HASH_LEN];
+ int res = -1;
+
+ /* key_schedule_context = concat(mode, psk_id_hash, info_hash) */
+ key_schedule_context[0] = HPKE_MODE_BASE;
+
+ /* psk_id_hash = LabeledExtract("", "psk_id_hash", psk_id) */
+ if (hpke_labeled_extract(ctx, false, NULL, 0, "psk_id_hash",
+ NULL, 0, &key_schedule_context[1]) < 0)
+ goto fail;
+
+ /* info_hash = LabeledExtract("", "info_hash", info) */
+ if (hpke_labeled_extract(ctx, false, NULL, 0, "info_hash",
+ info, info_len,
+ &key_schedule_context[1 + ctx->n_h]) < 0)
+ goto fail;
+
+ /* secret = LabeledExtract(shared_secret, "secret", psk) */
+ if (hpke_labeled_extract(ctx, false, shared_secret, ctx->n_secret,
+ "secret", NULL, 0, secret) < 0)
+ goto fail;
+
+ /* key = LabeledExpand(secret, "key", key_schedule_context, Nk) */
+ if (hpke_labeled_expand(ctx, false, secret, "key",
+ key_schedule_context, 1 + 2 * ctx->n_h,
+ ctx->key, ctx->n_k) < 0)
+ goto fail;
+
+ /* base_nonce = LabeledExpand(secret, "base_nonce",
+ * key_schedule_context, Nn) */
+ if (hpke_labeled_expand(ctx, false, secret, "base_nonce",
+ key_schedule_context, 1 + 2 * ctx->n_h,
+ ctx->base_nonce, ctx->n_n) < 0)
+ goto fail;
+ res = 0;
+fail:
+ forced_memzero(key_schedule_context, sizeof(key_schedule_context));
+ forced_memzero(secret, sizeof(secret));
+ return res;
+}
+
+
+static int hpke_encap(struct hpke_context *ctx, struct crypto_ec_key *pk_r,
+ u8 *shared_secret, u8 *enc)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ struct crypto_ec_key *sk_e;
+ int res = -1;
+ u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
+ size_t dhss_len;
+ struct wpabuf *enc_buf = NULL, *pk_rm = NULL;
+
+ /* skE, pkE = GenerateKeyPair() */
+ sk_e = crypto_ec_key_gen(ctx->iana_group);
+ if (!sk_e) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:Could not generate key pair",
+ __func__);
+ goto fail;
+ }
+
+ /* dh = DH(skE, pkR) */
+ dhss_len = sizeof(dhss);
+ pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_e, NULL);
+ if (!pctx ||
+ EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_r) != 1 ||
+ EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+ dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ /* enc = SerializePublicKey(pkE) */
+ enc_buf = crypto_ec_key_get_pubkey_point(sk_e, 1);
+ if (!enc_buf)
+ goto fail;
+ os_memcpy(enc, wpabuf_head(enc_buf), wpabuf_len(enc_buf));
+
+ /* pkRm = SerializePublicKey(pkR) */
+ pk_rm = crypto_ec_key_get_pubkey_point(pk_r, 1);
+ if (!pk_rm)
+ goto fail;
+
+ /* kem_context = concat(enc, pkRm) */
+ /* shared_secret = ExtractAndExpand(dh, kem_context) */
+ /* return shared_secret, enc */
+ res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+ wpabuf_head(pk_rm),
+ wpabuf_len(pk_rm), shared_secret);
+fail:
+ forced_memzero(dhss, sizeof(dhss));
+ crypto_ec_key_deinit(sk_e);
+ EVP_PKEY_CTX_free(pctx);
+ wpabuf_free(enc_buf);
+ wpabuf_free(pk_rm);
+ return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_seal(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len)
+{
+ EVP_CIPHER_CTX *cctx;
+ int len = 0;
+ struct wpabuf *ct = NULL;
+
+ /* No need to xor in sequence number since we support only the
+ * single-shot API, i.e., base_nonce can be used as-is. */
+
+ cctx = EVP_CIPHER_CTX_new();
+ if (!cctx ||
+ EVP_EncryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+ ctx->base_nonce) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+ __func__);
+ goto fail;
+ }
+ if (aad && aad_len &&
+ EVP_EncryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate(AAD) failed",
+ __func__);
+ goto fail;
+ }
+ ct = wpabuf_alloc(pt_len + AES_BLOCK_SIZE + ctx->n_t);
+ if (!ct)
+ goto fail;
+ if (EVP_EncryptUpdate(cctx, wpabuf_put(ct, 0), &len, pt, pt_len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_EncryptUpdate failed",
+ __func__);
+ goto fail;
+ }
+ wpabuf_put(ct, len);
+
+ if (EVP_EncryptFinal(cctx, wpabuf_put(ct, 0), &len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+ __func__);
+ wpabuf_free(ct);
+ ct = NULL;
+ goto fail;
+ }
+
+ if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_GET_TAG, ctx->n_t,
+ wpabuf_put(ct, ctx->n_t)) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:Could not get tag",
+ __func__);
+ wpabuf_free(ct);
+ ct = NULL;
+ goto fail;
+ }
+fail:
+ EVP_CIPHER_CTX_free(cctx);
+ return ct;
+}
+
+
+struct wpabuf * hpke_base_seal(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *peer_pub,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *pt, size_t pt_len)
+{
+ struct hpke_context *ctx;
+ u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+ u8 enc[1 + 2 * HPKE_MAX_PUB_LEN];
+ struct wpabuf *ct = NULL, *enc_ct = NULL;
+
+ ctx = hpke_get_context(kem_id, kdf_id, aead_id, peer_pub);
+ if (!ctx)
+ return NULL;
+
+ /* shared_secret, enc = Encap(pkR) */
+ if (hpke_encap(ctx, peer_pub, shared_secret, enc) < 0)
+ goto fail;
+
+ /* KeyScheduleS(mode_base, shared_secret, info,
+ * default_psk, default_psk_id) */
+ if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+ goto fail;
+
+ /* ct = ctx.Seal(aad, pt) */
+ ct = hpke_aead_seal(ctx, aad, aad_len, pt, pt_len);
+ if (!ct)
+ goto fail;
+
+ /* return enc, ct */
+ enc_ct = wpabuf_alloc(ctx->n_pk + wpabuf_len(ct));
+ if (!enc_ct)
+ goto fail;
+ wpabuf_put_data(enc_ct, enc, ctx->n_pk);
+ wpabuf_put_buf(enc_ct, ct);
+
+fail:
+ forced_memzero(shared_secret, sizeof(shared_secret));
+ hpke_free_context(ctx);
+ wpabuf_free(ct);
+ return enc_ct;
+}
+
+
+static int hpke_decap(struct hpke_context *ctx, const u8 *enc,
+ size_t enc_ct_len, struct crypto_ec_key *sk_r,
+ u8 *shared_secret)
+{
+ EVP_PKEY_CTX *pctx = NULL;
+ struct wpabuf *pk_rm = NULL;
+ size_t len;
+ int res = -1;
+ struct crypto_ec_key *pk_e = NULL;
+ u8 dhss[HPKE_MAX_SHARED_SECRET_LEN + 16];
+ size_t dhss_len;
+
+ /* pkE = DeserializePublicKey(enc) */
+ if (enc_ct_len < ctx->n_pk)
+ return -1; /* not enough room for enc */
+ if (enc[0] != 0x04)
+ return -1; /* not in uncompressed form */
+ len = (ctx->n_pk - 1) / 2;
+ pk_e = crypto_ec_key_set_pub(ctx->iana_group, &enc[1],
+ &enc[1 + len], len);
+ if (!pk_e)
+ return -1; /* invalid public key point */
+ /* dh = DH(skR, pkE) */
+ dhss_len = sizeof(dhss);
+ pctx = EVP_PKEY_CTX_new((EVP_PKEY *) sk_r, NULL);
+ if (!pctx ||
+ EVP_PKEY_derive_init(pctx) != 1 ||
+ EVP_PKEY_derive_set_peer(pctx, (EVP_PKEY *) pk_e) != 1 ||
+ EVP_PKEY_derive(pctx, dhss, &dhss_len) != 1 ||
+ dhss_len > HPKE_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_INFO, "OpenSSL: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ /* pkRm = SerializePublicKey(pk(skR)) */
+ pk_rm = crypto_ec_key_get_pubkey_point(sk_r, 1);
+ if (!pk_rm)
+ goto fail;
+
+ /* kem_context = concat(enc, pkRm) */
+ /* shared_secret = ExtractAndExpand(dh, kem_context) */
+ res = hpke_extract_and_expand(ctx, dhss, dhss_len, enc, ctx->n_pk,
+ wpabuf_head(pk_rm),
+ wpabuf_len(pk_rm), shared_secret);
+fail:
+ forced_memzero(dhss, sizeof(dhss));
+ crypto_ec_key_deinit(pk_e);
+ EVP_PKEY_CTX_free(pctx);
+ wpabuf_free(pk_rm);
+ return res;
+}
+
+
+static struct wpabuf *
+hpke_aead_open(struct hpke_context *ctx, const u8 *aad, size_t aad_len,
+ const u8 *ct, size_t ct_len)
+{
+ EVP_CIPHER_CTX *cctx;
+ int len = 0;
+ const u8 *tag;
+ struct wpabuf *pt = NULL;
+
+ if (ct_len < ctx->n_t)
+ return NULL;
+ tag = ct + ct_len - ctx->n_t;
+ ct_len -= ctx->n_t;
+
+ /* No need to xor in sequence number since we support only the
+ * single-shot API, i.e., base_nonce can be used as-is. */
+
+ cctx = EVP_CIPHER_CTX_new();
+ if (!cctx ||
+ EVP_DecryptInit_ex(cctx, ctx->cipher, NULL, ctx->key,
+ ctx->base_nonce) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptInit_ex failed",
+ __func__);
+ goto fail;
+ }
+ if (aad && aad_len &&
+ EVP_DecryptUpdate(cctx, NULL, &len, aad, aad_len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate(AAD) failed",
+ __func__);
+ goto fail;
+ }
+ pt = wpabuf_alloc(ct_len + AES_BLOCK_SIZE);
+ if (!pt)
+ goto fail;
+ if (EVP_DecryptUpdate(cctx, wpabuf_put(pt, 0), &len, ct, ct_len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptUpdate failed",
+ __func__);
+ goto fail;
+ }
+ wpabuf_put(pt, len);
+
+ if (EVP_CIPHER_CTX_ctrl(cctx, EVP_CTRL_AEAD_SET_TAG, ctx->n_t,
+ (void *) tag) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:Could not set tag",
+ __func__);
+ wpabuf_free(pt);
+ pt = NULL;
+ goto fail;
+ }
+
+ if (EVP_DecryptFinal(cctx, wpabuf_put(pt, 0), &len) != 1) {
+ wpa_printf(MSG_INFO, "OpenSSL:%s:EVP_DecryptFinal failed",
+ __func__);
+ wpabuf_free(pt);
+ pt = NULL;
+ }
+fail:
+ EVP_CIPHER_CTX_free(cctx);
+ return pt;
+}
+
+
+struct wpabuf * hpke_base_open(enum hpke_kem_id kem_id,
+ enum hpke_kdf_id kdf_id,
+ enum hpke_aead_id aead_id,
+ struct crypto_ec_key *own_priv,
+ const u8 *info, size_t info_len,
+ const u8 *aad, size_t aad_len,
+ const u8 *enc_ct, size_t enc_ct_len)
+{
+ struct hpke_context *ctx;
+ u8 shared_secret[HPKE_MAX_SHARED_SECRET_LEN];
+ struct wpabuf *pt = NULL;
+
+ ctx = hpke_get_context(kem_id, kdf_id, aead_id, own_priv);
+ if (!ctx)
+ return NULL;
+
+ /* shared_secret = Decap(enc, skR) */
+ if (hpke_decap(ctx, enc_ct, enc_ct_len, own_priv, shared_secret) < 0)
+ goto fail;
+
+ /* KeyScheduleR(mode_base, shared_secret, info,
+ * default_psk, default_psk_id) */
+ if (hpke_key_schedule(ctx, shared_secret, info, info_len) < 0)
+ goto fail;
+
+ /* return ctx.Open(aad, ct) */
+ pt = hpke_aead_open(ctx, aad, aad_len,
+ &enc_ct[ctx->n_pk], enc_ct_len - ctx->n_pk);
+
+fail:
+ forced_memzero(shared_secret, sizeof(shared_secret));
+ hpke_free_context(ctx);
+ return pt;
+}
+
+#endif /* CONFIG_DPP3 */
+
+
void crypto_unload(void)
{
openssl_unload_legacy_provider();
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 12b46b6..6bbf1ec 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -1000,6 +1000,17 @@
unsigned int key_mgmt_suite;
/**
+ * allowed_key_mgmts - Bitfield of allowed key management suites
+ * (WPA_KEY_MGMT_*) other than @key_mgmt_suite for the current
+ * connection
+ *
+ * SME in the driver may choose key_mgmt from this list for the initial
+ * connection or roaming. The driver which doesn't support this
+ * ignores this parameter.
+ */
+ unsigned int allowed_key_mgmts;
+
+ /**
* auth_alg - Allowed authentication algorithms
* Bit field of WPA_AUTH_ALG_*
*/
@@ -1875,6 +1886,8 @@
#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
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY 0x01000000
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY 0x02000000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
unsigned int key_mgmt_iftype[WPA_IF_MAX];
@@ -2053,15 +2066,15 @@
#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
-/** Driver supports secure LTF */
-#define WPA_DRIVER_FLAGS2_SEC_LTF 0x0000000000000004ULL
-/** Driver supports secure RTT measurement exchange */
-#define WPA_DRIVER_FLAGS2_SEC_RTT 0x0000000000000008ULL
+/** Driver supports secure LTF in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_AP 0x0000000000000004ULL
+/** Driver supports secure RTT measurement exchange in AP mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_AP 0x0000000000000008ULL
/**
* Driver supports protection of range negotiation and measurement management
- * frames
+ * frames in AP mode
*/
-#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG 0x0000000000000010ULL
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP 0x0000000000000010ULL
/** Driver supports Beacon frame TX rate configuration (HE rates) */
#define WPA_DRIVER_FLAGS2_BEACON_RATE_HE 0x0000000000000020ULL
/** Driver supports Beacon protection only in client mode */
@@ -2074,6 +2087,15 @@
#define WPA_DRIVER_FLAGS2_SA_QUERY_OFFLOAD_AP 0x0000000000000200ULL
/** Driver supports background radar/CAC detection */
#define WPA_DRIVER_RADAR_BACKGROUND 0x0000000000000400ULL
+/** Driver supports secure LTF in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_LTF_STA 0x0000000000000800ULL
+/** Driver supports secure RTT measurement exchange in STA mode */
+#define WPA_DRIVER_FLAGS2_SEC_RTT_STA 0x0000000000001000ULL
+/**
+ * Driver supports protection of range negotiation and measurement management
+ * frames in STA mode
+ */
+#define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA 0x0000000000002000ULL
u64 flags2;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -2186,6 +2208,9 @@
/* Maximum number of supported CSA counters */
u16 max_csa_counters;
+
+ /* Maximum number of supported AKM suites in commands */
+ unsigned int max_num_akms;
};
@@ -2373,6 +2398,11 @@
int center_frq2;
};
+struct wpa_mlo_signal_info {
+ u16 valid_links;
+ struct wpa_signal_info links[MAX_NUM_MLD_LINKS];
+};
+
/**
* struct wpa_channel_info - Information about the current channel
* @frequency: Center frequency of the primary 20 MHz channel
@@ -2605,6 +2635,90 @@
const u8 *pmkid;
};
+#define WPAS_MAX_PASN_PEERS 10
+
+enum pasn_status {
+ PASN_STATUS_SUCCESS = 0,
+ PASN_STATUS_FAILURE = 1,
+};
+
+/**
+ * struct pasn_peer - PASN peer parameters
+ *
+ * Used to process the PASN authentication event from the driver to
+ * userspace and to send a response back.
+ * @own_addr: Own MAC address specified by the driver to use for PASN
+ * handshake.
+ * @peer_addr: MAC address of the peer with which PASN authentication is to be
+ * performed.
+ * @network_id: Unique id for the network.
+ * This identifier is used as a unique identifier for each network
+ * block when using the control interface. Each network is allocated an
+ * id when it is being created, either when reading the configuration
+ * file or when a new network is added through the control interface.
+ * @akmp: Authentication key management protocol type supported.
+ * @cipher: Cipher suite.
+ * @group: Finite cyclic group. Default group used is 19 (ECC).
+ * @ltf_keyseed_required: Indicates whether LTF keyseed generation is required
+ * @status: PASN response status, %PASN_STATUS_SUCCESS for successful
+ * authentication, use %PASN_STATUS_FAILURE if PASN authentication
+ * fails or if wpa_supplicant fails to set the security ranging context to
+ * the driver
+ */
+struct pasn_peer {
+ u8 own_addr[ETH_ALEN];
+ u8 peer_addr[ETH_ALEN];
+ int network_id;
+ int akmp;
+ int cipher;
+ int group;
+ bool ltf_keyseed_required;
+ enum pasn_status status;
+};
+
+/**
+ * struct pasn_auth - PASN authentication trigger parameters
+ *
+ * These are used across the PASN authentication event from the driver to
+ * userspace and to send a response to it.
+ * @action: Action type. Only significant for the event interface.
+ * @num_peers: The number of peers for which the PASN handshake is requested
+ * for.
+ * @peer: Holds the peer details.
+ */
+struct pasn_auth {
+ enum {
+ PASN_ACTION_AUTH,
+ PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT,
+ } action;
+ unsigned int num_peers;
+ struct pasn_peer peer[WPAS_MAX_PASN_PEERS];
+};
+
+/**
+ * struct secure_ranging_params - Parameters required to set secure ranging
+ * context for a peer.
+ *
+ * @action: Add or delete a security context to the driver.
+ * @own_addr: Own MAC address used during key derivation.
+ * @peer_addr: Address of the peer device.
+ * @cipher: Cipher suite.
+ * @tk_len: Length of temporal key.
+ * @tk: Temporal key buffer.
+ * @ltf_keyseed_len: Length of LTF keyseed.
+ * @ltf_keyeed: LTF keyseed buffer.
+ */
+struct secure_ranging_params {
+ u32 action;
+ const u8 *own_addr;
+ const u8 *peer_addr;
+ u32 cipher;
+ u8 tk_len;
+ const u8 *tk;
+ u8 ltf_keyseed_len;
+ const u8 *ltf_keyseed;
+};
+
/* enum nested_attr - Used to specify if subcommand uses nested attributes */
enum nested_attr {
NESTED_ATTR_NOT_USED = 0,
@@ -2630,6 +2744,16 @@
u32 flag; /* bitmap for WEIGHTED_PCL_* */
};
+struct driver_sta_mlo_info {
+ u16 valid_links; /* bitmap of valid link IDs */
+ u8 ap_mld_addr[ETH_ALEN];
+ struct {
+ u8 addr[ETH_ALEN];
+ u8 bssid[ETH_ALEN];
+ unsigned int freq;
+ } links[MAX_NUM_MLD_LINKS];
+};
+
/**
* struct wpa_driver_ops - Driver interface API definition
*
@@ -3877,6 +4001,14 @@
int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
/**
+ * mlo_signal_poll - Get current MLO connection information
+ * @priv: Private driver interface data
+ * @mlo_signal_info: MLO connection info structure
+ */
+ int (*mlo_signal_poll)(void *priv,
+ struct wpa_mlo_signal_info *mlo_signal_info);
+
+ /**
* channel_info - Get parameters of the current operating channel
* @priv: Private driver interface data
* @channel_info: Channel info structure
@@ -4673,6 +4805,37 @@
*/
int (*dpp_listen)(void *priv, bool enable);
+ /**
+ * set_secure_ranging_ctx - Add or delete secure ranging parameters of
+ * the specified peer to the driver.
+ * @priv: Private driver interface data
+ * @params: Secure ranging parameters
+ * Returns: 0 on success, -1 on failure
+ *
+ */
+ int (*set_secure_ranging_ctx)(void *priv,
+ struct secure_ranging_params *params);
+
+ /**
+ * send_pasn_resp - Send PASN response for a set of peers to the
+ * driver.
+ * @priv: Private driver interface data
+ * @params: Parameters holding peers and respective status.
+ * Returns: 0 on success, -1 on failure
+ */
+ int (*send_pasn_resp)(void *priv, struct pasn_auth *params);
+
+ /**
+ * get_sta_mlo_info - Get the current multi-link association info
+ * @priv: Private driver interface data
+ * @mlo: Pointer to fill multi-link association info
+ * Returns: 0 on success, -1 on failure
+ *
+ * This callback is used to fetch multi-link of the current association.
+ */
+ int (*get_sta_mlo_info)(void *priv,
+ struct driver_sta_mlo_info *mlo_info);
+
#ifdef CONFIG_TESTING_OPTIONS
int (*register_frame)(void *priv, u16 type,
const u8 *match, size_t match_len,
@@ -5266,6 +5429,30 @@
* EVENT_CCA_NOTIFY - Notification that CCA has completed
*/
EVENT_CCA_NOTIFY,
+
+ /**
+ * EVENT_PASN_AUTH - This event is used by the driver that requests
+ * PASN authentication and secure ranging context for multiple peers.
+ */
+ EVENT_PASN_AUTH,
+
+ /**
+ * EVENT_LINK_CH_SWITCH - MLD AP link decided to switch channels
+ *
+ * Described in wpa_event_data.ch_switch.
+ *
+ */
+ EVENT_LINK_CH_SWITCH,
+
+ /**
+ * EVENT_LINK_CH_SWITCH_STARTED - MLD AP link started to switch channels
+ *
+ * This is a pre-switch event indicating the shortly following switch
+ * of operating channels.
+ *
+ * Described in wpa_event_data.ch_switch.
+ */
+ EVENT_LINK_CH_SWITCH_STARTED,
};
@@ -5981,6 +6168,7 @@
* @ch_width: Channel width
* @cf1: Center frequency 1
* @cf2: Center frequency 2
+ * @link_id: Link ID of the MLO link
*/
struct ch_switch {
int freq;
@@ -5989,6 +6177,7 @@
enum chan_width ch_width;
int cf1;
int cf2;
+ int link_id;
} ch_switch;
/**
@@ -6166,6 +6355,12 @@
struct bss_color_collision {
u64 bitmap;
} bss_color_collision;
+
+ /**
+ * struct pasn_auth - Data for EVENT_PASN_AUTH
+ */
+ struct pasn_auth pasn_auth;
+
};
/**
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index 84e6a9e..0ac0aa6 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -95,6 +95,9 @@
E2S(CCA_STARTED_NOTIFY);
E2S(CCA_ABORTED_NOTIFY);
E2S(CCA_NOTIFY);
+ E2S(PASN_AUTH);
+ E2S(LINK_CH_SWITCH);
+ E2S(LINK_CH_SWITCH_STARTED);
}
return "UNKNOWN";
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index bb3455d..1385edb 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -31,6 +31,8 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_common.h"
+#include "crypto/sha256.h"
+#include "crypto/sha384.h"
#include "netlink.h"
#include "linux_defines.h"
#include "linux_ioctl.h"
@@ -272,8 +274,13 @@
if (drv->associated)
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
drv->associated = 0;
+ drv->sta_mlo_info.valid_links = 0;
os_memset(drv->bssid, 0, ETH_ALEN);
drv->first_bss->freq = 0;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ os_free(drv->pending_roam_data);
+ drv->pending_roam_data = NULL;
+#endif /* CONFIG_DRIVER_NL80211_QCA */
}
@@ -1017,6 +1024,20 @@
}
+static int nl80211_get_sta_mlo_info(void *priv,
+ struct driver_sta_mlo_info *mlo_info)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ if (!drv->associated)
+ return -1;
+
+ os_memcpy(mlo_info, &drv->sta_mlo_info, sizeof(*mlo_info));
+ return 0;
+}
+
+
static void wpa_driver_nl80211_event_newlink(
struct nl80211_global *global, struct wpa_driver_nl80211_data *drv,
int ifindex, const char *ifname)
@@ -1442,6 +1463,8 @@
u8 assoc_bssid[ETH_ALEN];
u8 assoc_ssid[SSID_MAX_LEN];
u8 assoc_ssid_len;
+ u8 bssid[MAX_NUM_MLD_LINKS][ETH_ALEN];
+ unsigned int freq[MAX_NUM_MLD_LINKS];
};
static int nl80211_get_assoc_freq_handler(struct nl_msg *msg, void *arg)
@@ -1454,9 +1477,11 @@
[NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
[NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
[NL80211_BSS_STATUS] = { .type = NLA_U32 },
+ [NL80211_BSS_MLO_LINK_ID] = { .type = NLA_U8 },
};
struct nl80211_get_assoc_freq_arg *ctx = arg;
enum nl80211_bss_status status;
+ struct wpa_driver_nl80211_data *drv = ctx->drv;
nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
genlmsg_attrlen(gnlh, 0), NULL);
@@ -1469,9 +1494,25 @@
status = nla_get_u32(bss[NL80211_BSS_STATUS]);
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_FREQUENCY]) {
- ctx->assoc_freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
- wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
- ctx->assoc_freq);
+ int link_id = -1;
+ u32 freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+
+ if (bss[NL80211_BSS_MLO_LINK_ID])
+ link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+ if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+ ctx->freq[link_id] = freq;
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLO link %d associated on %u MHz",
+ link_id, ctx->freq[link_id]);
+ }
+
+ if (!drv->sta_mlo_info.valid_links ||
+ drv->mlo_assoc_link_id == link_id) {
+ ctx->assoc_freq = freq;
+ wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+ ctx->assoc_freq);
+ }
}
if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
bss[NL80211_BSS_FREQUENCY]) {
@@ -1481,10 +1522,26 @@
}
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
bss[NL80211_BSS_BSSID]) {
- os_memcpy(ctx->assoc_bssid,
- nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
- wpa_printf(MSG_DEBUG, "nl80211: Associated with "
- MACSTR, MAC2STR(ctx->assoc_bssid));
+ int link_id = -1;
+ const u8 *bssid = nla_data(bss[NL80211_BSS_BSSID]);
+
+ if (bss[NL80211_BSS_MLO_LINK_ID])
+ link_id = nla_get_u8(bss[NL80211_BSS_MLO_LINK_ID]);
+
+ if (link_id >= 0 && link_id < MAX_NUM_MLD_LINKS) {
+ os_memcpy(ctx->bssid[link_id], bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG,
+ "nl80211: MLO link %d associated with "
+ MACSTR, link_id, MAC2STR(bssid));
+ }
+
+ if (!drv->sta_mlo_info.valid_links ||
+ drv->mlo_assoc_link_id == link_id) {
+ os_memcpy(ctx->assoc_bssid, bssid, ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+ MACSTR, MAC2STR(bssid));
+ }
+
}
if (status == NL80211_BSS_STATUS_ASSOCIATED &&
@@ -1570,6 +1627,14 @@
"associated BSS from scan results: %u MHz", freq);
if (freq)
drv->assoc_freq = freq;
+
+ if (drv->sta_mlo_info.valid_links) {
+ int i;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++)
+ drv->sta_mlo_info.links[i].freq = arg.freq[i];
+ }
+
return drv->assoc_freq;
}
wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
@@ -1641,7 +1706,7 @@
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- struct wpa_signal_info *sig)
+ const u8 *bssid, struct wpa_signal_info *sig)
{
struct nl_msg *msg;
@@ -1649,7 +1714,7 @@
sig->current_txrate = 0;
if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) {
nlmsg_free(msg);
return -ENOBUFS;
}
@@ -2387,8 +2452,7 @@
#ifdef CONFIG_PASN
/* register for PASN Authentication frames */
- if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
- nl80211_register_frame(bss, bss->nl_mgmt, type,
+ if (nl80211_register_frame(bss, bss->nl_mgmt, type,
(u8 *) "\x07\x00", 2, false))
ret = -1;
#endif /* CONFIG_PASN */
@@ -3178,7 +3242,9 @@
__AKM(IEEE8021X_SHA256, 802_1X_SHA256);
__AKM(PSK_SHA256, PSK_SHA256);
__AKM(SAE, SAE);
+ __AKM(SAE_EXT_KEY, SAE_EXT_KEY);
__AKM(FT_SAE, FT_SAE);
+ __AKM(FT_SAE_EXT_KEY, FT_SAE_EXT_KEY);
__AKM(CCKM, CCKM);
__AKM(OSEN, OSEN);
__AKM(IEEE8021X_SUITE_B, 802_1X_SUITE_B);
@@ -4777,7 +4843,8 @@
if (drv->device_ap_sme) {
u32 flags = 0;
- if (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) {
+ if (params->key_mgmt_suites & (WPA_KEY_MGMT_SAE |
+ WPA_KEY_MGMT_SAE_EXT_KEY)) {
/* Add the previously used flag attribute to support
* older kernel versions and the newer flag bit for
* newer kernels. */
@@ -4948,8 +5015,7 @@
#endif /* CONFIG_IEEE80211AX */
#ifdef CONFIG_SAE
- if (((params->key_mgmt_suites & WPA_KEY_MGMT_SAE) ||
- (params->key_mgmt_suites & WPA_KEY_MGMT_FT_SAE)) &&
+ if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail;
#endif /* CONFIG_SAE */
@@ -6245,6 +6311,17 @@
}
+static unsigned int num_bits_set(u32 val)
+{
+ unsigned int c;
+
+ for (c = 0; val; c++)
+ val &= val - 1;
+
+ return c;
+}
+
+
static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
struct wpa_driver_associate_params *params,
struct nl_msg *msg)
@@ -6364,7 +6441,9 @@
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_SAE_EXT_KEY ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE_EXT_KEY ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
@@ -6374,71 +6453,111 @@
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_OWE ||
params->key_mgmt_suite == WPA_KEY_MGMT_DPP) {
- int mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ u32 *mgmt;
+ unsigned int akm_count = 1, i;
+
+ /*
+ * Make sure the driver has capability to handle default AKM in
+ * key_mgmt_suite plus allowed AKMs in allowed_key_mgmts.
+ */
+ if (drv->capa.max_num_akms <=
+ num_bits_set(params->allowed_key_mgmts)) {
+ wpa_printf(MSG_INFO,
+ "nl80211: Not enough support for the allowed AKMs (max_num_akms=%u <= num_bits_set=%u)",
+ drv->capa.max_num_akms,
+ num_bits_set(params->allowed_key_mgmts));
+ return -1;
+ }
+
+ mgmt = os_malloc(sizeof(u32) * drv->capa.max_num_akms);
+ if (!mgmt)
+ return -1;
+
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
switch (params->key_mgmt_suite) {
case WPA_KEY_MGMT_CCKM:
- mgmt = RSN_AUTH_KEY_MGMT_CCKM;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_CCKM;
break;
case WPA_KEY_MGMT_IEEE8021X:
- mgmt = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
break;
case WPA_KEY_MGMT_FT_IEEE8021X:
- mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X;
break;
case WPA_KEY_MGMT_FT_PSK:
- mgmt = RSN_AUTH_KEY_MGMT_FT_PSK;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_PSK;
break;
case WPA_KEY_MGMT_IEEE8021X_SHA256:
- mgmt = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SHA256;
break;
case WPA_KEY_MGMT_PSK_SHA256:
- mgmt = RSN_AUTH_KEY_MGMT_PSK_SHA256;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_SHA256;
break;
case WPA_KEY_MGMT_OSEN:
- mgmt = RSN_AUTH_KEY_MGMT_OSEN;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_OSEN;
break;
case WPA_KEY_MGMT_SAE:
- mgmt = RSN_AUTH_KEY_MGMT_SAE;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_SAE;
+ break;
+ case WPA_KEY_MGMT_SAE_EXT_KEY:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_SAE_EXT_KEY;
break;
case WPA_KEY_MGMT_FT_SAE:
- mgmt = RSN_AUTH_KEY_MGMT_FT_SAE;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE;
+ break;
+ case WPA_KEY_MGMT_FT_SAE_EXT_KEY:
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
- mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
- mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
break;
case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
- mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
break;
case WPA_KEY_MGMT_FILS_SHA256:
- mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA256;
break;
case WPA_KEY_MGMT_FILS_SHA384:
- mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA384;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FILS_SHA384;
break;
case WPA_KEY_MGMT_FT_FILS_SHA256:
- mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA256;
break;
case WPA_KEY_MGMT_FT_FILS_SHA384:
- mgmt = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_FT_FILS_SHA384;
break;
case WPA_KEY_MGMT_OWE:
- mgmt = RSN_AUTH_KEY_MGMT_OWE;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_OWE;
break;
case WPA_KEY_MGMT_DPP:
- mgmt = RSN_AUTH_KEY_MGMT_DPP;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_DPP;
break;
case WPA_KEY_MGMT_PSK:
default:
- mgmt = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
+ mgmt[0] = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
break;
}
- wpa_printf(MSG_DEBUG, " * akm=0x%x", mgmt);
- if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
+
+ if (drv->capa.max_num_akms > 1) {
+ akm_count += wpa_key_mgmt_to_suites(
+ params->allowed_key_mgmts, &mgmt[1],
+ drv->capa.max_num_akms - 1);
+ }
+
+ for (i = 0; i < akm_count; i++)
+ wpa_printf(MSG_DEBUG, " * akm[%d]=0x%x", i, mgmt[i]);
+
+ if (nla_put(msg, NL80211_ATTR_AKM_SUITES,
+ akm_count * sizeof(u32), mgmt)) {
+ os_free(mgmt);
return -1;
+ }
+
+ os_free(mgmt);
}
#ifdef CONFIG_DRIVER_NL80211_BRCM
@@ -6519,13 +6638,13 @@
nl80211_put_fils_connect_params(drv, params, msg) != 0)
return -1;
- if ((
#ifdef CONFIG_DRIVER_NL80211_BRCM
- (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) &&
#else
- params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM */
- params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
(!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) &&
nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
return -1;
@@ -6546,9 +6665,8 @@
#ifdef CONFIG_DRIVER_NL80211_QCA
if (params->req_key_mgmt_offload && params->psk &&
- (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
- params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
- params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+ (wpa_key_mgmt_wpa_psk_no_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_wpa_psk_no_sae(params->allowed_key_mgmts))) {
wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
ret = issue_key_mgmt_set_key(drv, params->psk, 32);
if (ret)
@@ -6575,13 +6693,13 @@
goto fail;
#ifdef CONFIG_SAE
- if ((
#ifdef CONFIG_DRIVER_NL80211_BRCM
- (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ if (((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)) &&
#else
- params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ if ((wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts)) &&
#endif /* CONFIG_DRIVER_NL80211_BRCM */
- params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE) &&
nl80211_put_sae_pwe(msg, params->sae_pwe) < 0)
goto fail;
#endif /* CONFIG_SAE */
@@ -6689,13 +6807,13 @@
if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
return -1;
- if (
#ifdef CONFIG_DRIVER_NL80211_BRCM
- (params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ if ((params->key_mgmt_suite & WPA_KEY_MGMT_SAE) ||
+ (params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE))
#else
- params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ if (wpa_key_mgmt_sae(params->key_mgmt_suite) ||
+ wpa_key_mgmt_sae(params->allowed_key_mgmts))
#endif /* CONFIG_DRIVER_NL80211_BRCM */
- params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE)
bss->use_nl_connect = 1;
else
bss->use_nl_connect = 0;
@@ -7009,14 +7127,17 @@
struct nl_msg *msg;
struct nl80211_sta_flag_update upd;
int ret;
+ const u8 *connected_addr = drv->sta_mlo_info.valid_links ?
+ drv->sta_mlo_info.ap_mld_addr : drv->bssid;
- if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+ if (!drv->associated && is_zero_ether_addr(connected_addr) &&
+ !authorized) {
wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
return 0;
}
wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
- MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
+ MACSTR, authorized ? "" : "un", MAC2STR(connected_addr));
os_memset(&upd, 0, sizeof(upd));
upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
@@ -7024,7 +7145,7 @@
upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
- nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+ nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, connected_addr) ||
nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
nlmsg_free(msg);
return -ENOBUFS;
@@ -8788,7 +8909,7 @@
int res;
os_memset(si, 0, sizeof(*si));
- res = nl80211_get_link_signal(drv, si);
+ res = nl80211_get_link_signal(drv, drv->bssid, si);
if (res) {
if (drv->nlmode != NL80211_IFTYPE_ADHOC &&
drv->nlmode != NL80211_IFTYPE_MESH_POINT)
@@ -8804,6 +8925,163 @@
}
+static int get_links_noise(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ };
+ struct wpa_mlo_signal_info *mlo_sig = arg;
+ int i;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+ wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+ return NL_SKIP;
+ }
+
+ if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ tb[NL80211_ATTR_SURVEY_INFO],
+ survey_policy)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to parse nested attributes");
+ return NL_SKIP;
+ }
+
+ if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ return NL_SKIP;
+
+ if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+ return NL_SKIP;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo_sig->valid_links & BIT(i)))
+ continue;
+
+ if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+ mlo_sig->links[i].frequency)
+ continue;
+
+ mlo_sig->links[i].current_noise =
+ (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+ break;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_links_noise(struct wpa_driver_nl80211_data *drv,
+ struct wpa_mlo_signal_info *mlo_sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+ return send_and_recv_msgs(drv, msg, get_links_noise, mlo_sig,
+ NULL, NULL);
+}
+
+
+static int get_links_channel_width(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_mlo_signal_info *mlo_sig = arg;
+ struct nlattr *link;
+ int rem_links;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINKS])
+ return NL_SKIP;
+
+ nla_for_each_nested(link, tb[NL80211_ATTR_MLO_LINKS], rem_links) {
+ struct nlattr *tb2[NL80211_ATTR_MAX + 1];
+ int link_id;
+
+ nla_parse(tb2, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+ NULL);
+
+ if (!tb2[NL80211_ATTR_MLO_LINK_ID])
+ continue;
+
+ link_id = nla_get_u8(tb2[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ if (!tb2[NL80211_ATTR_CHANNEL_WIDTH])
+ continue;
+ mlo_sig->links[link_id].chanwidth = convert2width(
+ nla_get_u32(tb2[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb2[NL80211_ATTR_CENTER_FREQ1])
+ mlo_sig->links[link_id].center_frq1 =
+ nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb2[NL80211_ATTR_CENTER_FREQ2])
+ mlo_sig->links[link_id].center_frq2 =
+ nla_get_u32(tb2[NL80211_ATTR_CENTER_FREQ2]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_get_links_channel_width(struct wpa_driver_nl80211_data *drv,
+ struct wpa_mlo_signal_info *mlo_sig)
+{
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_links_channel_width, mlo_sig,
+ NULL, NULL);
+}
+
+
+static int nl80211_mlo_signal_poll(void *priv,
+ struct wpa_mlo_signal_info *mlo_si)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ int res;
+ int i;
+
+ if (drv->nlmode != NL80211_IFTYPE_STATION ||
+ !drv->sta_mlo_info.valid_links)
+ return -1;
+
+ os_memset(mlo_si, 0, sizeof(*mlo_si));
+ mlo_si->valid_links = drv->sta_mlo_info.valid_links;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo_si->valid_links & BIT(i)))
+ continue;
+
+ res = nl80211_get_link_signal(drv,
+ drv->sta_mlo_info.links[i].bssid,
+ &mlo_si->links[i]);
+ if (res != 0)
+ return res;
+
+ mlo_si->links[i].center_frq1 = -1;
+ mlo_si->links[i].center_frq2 = -1;
+ mlo_si->links[i].chanwidth = CHAN_WIDTH_UNKNOWN;
+ mlo_si->links[i].current_noise = WPA_INVALID_NOISE;
+ mlo_si->links[i].frequency = drv->sta_mlo_info.links[i].freq;
+ }
+
+ res = nl80211_get_links_channel_width(drv, mlo_si);
+ if (res != 0)
+ return res;
+
+ return nl80211_get_links_noise(drv, mlo_si);
+}
+
+
static int nl80211_set_param(void *priv, const char *param)
{
struct i802_bss *bss = priv;
@@ -9892,6 +10170,34 @@
return pos - buf;
pos += res;
+ if (drv->sta_mlo_info.valid_links) {
+ int i;
+ struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+ res = os_snprintf(pos, end - pos,
+ "ap_mld_addr=" MACSTR "\n",
+ MAC2STR(mlo->ap_mld_addr));
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ for (i = 0; i < MAX_NUM_MLD_LINKS; i++) {
+ if (!(mlo->valid_links & BIT(i)))
+ continue;
+
+ res = os_snprintf(pos, end - pos,
+ "link_addr[%u]=" MACSTR "\n"
+ "link_bssid[%u]=" MACSTR "\n"
+ "link_freq[%u]=%u\n",
+ i, MAC2STR(mlo->links[i].addr),
+ i, MAC2STR(mlo->links[i].bssid),
+ i, mlo->links[i].freq);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+ }
+
if (drv->has_capability) {
res = os_snprintf(pos, end - pos,
"capa.key_mgmt=0x%x\n"
@@ -12084,6 +12390,169 @@
#endif /* CONFIG_MBO */
+
+#ifdef CONFIG_PASN
+
+static int nl80211_send_pasn_resp(void *priv, struct pasn_auth *params)
+{
+ unsigned int i;
+ struct i802_bss *bss = priv;
+ struct nl_msg *msg = NULL;
+ struct nlattr *nlpeers, *attr, *attr1;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: PASN authentication response for %d entries",
+ params->num_peers);
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_PASN))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ nlpeers = nla_nest_start(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEERS);
+ if (!nlpeers)
+ goto fail;
+
+ for (i = 0; i < params->num_peers; i++) {
+ attr1 = nla_nest_start(msg, i);
+ if (!attr1 ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR,
+ ETH_ALEN, params->peer[i].own_addr) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR,
+ ETH_ALEN, params->peer[i].peer_addr))
+ goto fail;
+
+ if (params->peer[i].status == 0)
+ nla_put_flag(msg,
+ QCA_WLAN_VENDOR_ATTR_PASN_PEER_STATUS_SUCCESS);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Own address[%u]: " MACSTR
+ " Peer address[%u]: " MACSTR " Status: %s",
+ i, MAC2STR(params->peer[i].own_addr), i,
+ MAC2STR(params->peer[i].peer_addr),
+ params->peer[i].status ? "Fail" : "Success");
+ nla_nest_end(msg, attr1);
+ }
+
+ nla_nest_end(msg, nlpeers);
+ nla_nest_end(msg, attr);
+
+ return send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+
+static u32 wpa_ltf_keyseed_len_to_sha_type(size_t len)
+{
+ if (len == SHA384_MAC_LEN)
+ return QCA_WLAN_VENDOR_SHA_384;
+ if (len == SHA256_MAC_LEN)
+ return QCA_WLAN_VENDOR_SHA_256;
+
+ wpa_printf(MSG_ERROR, "nl80211: Unexpected LTF keyseed len %zu", len);
+ return (u32) -1;
+}
+
+
+static int nl80211_set_secure_ranging_ctx(void *priv,
+ struct secure_ranging_params *params)
+{
+ int ret;
+ u32 suite;
+ struct nlattr *attr;
+ struct nl_msg *msg = NULL;
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+
+ /* Configure secure ranging context only to the drivers that support it.
+ */
+ if (!drv->secure_ranging_ctx_vendor_cmd_avail)
+ return 0;
+
+ if (!params->peer_addr || !params->own_addr)
+ return -1;
+
+ wpa_dbg(drv->ctx, MSG_DEBUG,
+ "nl80211: Secure ranging context for " MACSTR,
+ MAC2STR(params->peer_addr));
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR);
+ if (!msg ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT))
+ goto fail;
+
+ attr = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA);
+ if (!attr)
+ goto fail;
+
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_PEER_MAC_ADDR,
+ ETH_ALEN, params->peer_addr) ||
+ nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SRC_ADDR,
+ ETH_ALEN, params->own_addr) ||
+ nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_ACTION,
+ params->action))
+ goto fail;
+
+ if (params->cipher) {
+ suite = wpa_cipher_to_cipher_suite(params->cipher);
+ if (!suite ||
+ nla_put_u32(msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_CIPHER,
+ suite))
+ goto fail;
+ }
+
+ if (params->tk_len && params->tk) {
+ if (nla_put(msg, QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_TK,
+ params->tk_len, params->tk))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: TK",
+ params->tk, params->tk_len);
+ }
+
+ if (params->ltf_keyseed_len && params->ltf_keyseed) {
+ u32 sha_type = wpa_ltf_keyseed_len_to_sha_type(
+ params->ltf_keyseed_len);
+
+ if (sha_type == (u32) -1 ||
+ nla_put_u32(
+ msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_SHA_TYPE,
+ sha_type) ||
+ nla_put(msg,
+ QCA_WLAN_VENDOR_ATTR_SECURE_RANGING_CTX_LTF_KEYSEED,
+ params->ltf_keyseed_len, params->ltf_keyseed))
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "nl80211: LTF keyseed",
+ params->ltf_keyseed, params->ltf_keyseed_len);
+ }
+ nla_nest_end(msg, attr);
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL, NULL, NULL);
+ if (ret)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Set secure ranging context failed: ret=%d (%s)",
+ ret, strerror(-ret));
+ return ret;
+fail:
+ nlmsg_free(msg);
+ return -1;
+}
+
+#endif /* CONFIG_PASN */
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
static int nl80211_do_acs(void *priv, struct drv_acs_params *params)
@@ -12573,6 +13042,7 @@
.resume = wpa_driver_nl80211_resume,
.signal_monitor = nl80211_signal_monitor,
.signal_poll = nl80211_signal_poll,
+ .mlo_signal_poll = nl80211_mlo_signal_poll,
.channel_info = nl80211_channel_info,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
@@ -12643,6 +13113,10 @@
#endif /* CONFIG_MBO */
.set_bssid_tmp_disallow = nl80211_set_bssid_tmp_disallow,
.add_sta_node = nl80211_add_sta_node,
+#ifdef CONFIG_PASN
+ .send_pasn_resp = nl80211_send_pasn_resp,
+ .set_secure_ranging_ctx = nl80211_set_secure_ranging_ctx,
+#endif /* CONFIG_PASN */
#endif /* CONFIG_DRIVER_NL80211_QCA */
.do_acs = nl80211_do_acs,
.configure_data_frame_filters = nl80211_configure_data_frame_filters,
@@ -12653,6 +13127,7 @@
#ifdef CONFIG_DPP
.dpp_listen = nl80211_dpp_listen,
#endif /* CONFIG_DPP */
+ .get_sta_mlo_info = nl80211_get_sta_mlo_info,
#ifdef CONFIG_TESTING_OPTIONS
.register_frame = testing_nl80211_register_frame,
.radio_disable = testing_nl80211_radio_disable,
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index 8ad7184..be5783b 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -128,6 +128,8 @@
u8 bssid[ETH_ALEN];
u8 prev_bssid[ETH_ALEN];
int associated;
+ int mlo_assoc_link_id;
+ struct driver_sta_mlo_info sta_mlo_info;
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
enum nl80211_iftype nlmode;
@@ -180,6 +182,8 @@
unsigned int unsol_bcast_probe_resp:1;
unsigned int qca_do_acs:1;
unsigned int brcm_do_acs:1;
+ unsigned int uses_6ghz:1;
+ unsigned int secure_ranging_ctx_vendor_cmd_avail:1;
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
@@ -235,7 +239,6 @@
bool roam_indication_done;
u8 *pending_roam_data;
size_t pending_roam_data_len;
- struct os_reltime pending_roam_ind_time;
#endif /* CONFIG_DRIVER_NL80211_QCA */
};
@@ -269,7 +272,7 @@
int is_sta_interface(enum nl80211_iftype nlmode);
int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
- struct wpa_signal_info *sig);
+ const u8 *bssid, struct wpa_signal_info *sig);
int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
struct wpa_signal_info *sig_change);
int nl80211_get_wiphy_index(struct i802_bss *bss);
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 75df36c..46592b3 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -294,6 +294,9 @@
case RSN_AUTH_KEY_MGMT_FT_SAE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE;
break;
+ case RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_SAE_EXT_KEY;
+ break;
case RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_802_1X_SHA384;
break;
@@ -330,6 +333,9 @@
case RSN_AUTH_KEY_MGMT_SAE:
key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
break;
+ case RSN_AUTH_KEY_MGMT_SAE_EXT_KEY:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE_EXT_KEY;
+ break;
}
}
@@ -668,6 +674,25 @@
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_RADAR_BACKGROUND))
capa->flags2 |= WPA_DRIVER_RADAR_BACKGROUND;
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SECURE_LTF)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ }
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_SECURE_RTT)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+ }
+
+ if (ext_feature_isset(
+ ext_features, len,
+ NL80211_EXT_FEATURE_PROT_RANGE_NEGO_AND_MEASURE)) {
+ capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+ capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
+ }
}
@@ -1029,6 +1054,9 @@
case QCA_NL80211_VENDOR_SUBCMD_GET_STA_INFO:
drv->get_sta_info_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_SECURE_RANGING_CONTEXT:
+ drv->secure_ranging_ctx_vendor_cmd_avail = 1;
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
#if defined(CONFIG_DRIVER_NL80211_BRCM) || defined(CONFIG_DRIVER_NL80211_SYNA)
@@ -1086,6 +1114,10 @@
if (tb[NL80211_ATTR_WIPHY_SELF_MANAGED_REG])
capa->flags |= WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY;
+ if (tb[NL80211_ATTR_MAX_NUM_AKM_SUITES])
+ capa->max_num_akms =
+ nla_get_u16(tb[NL80211_ATTR_MAX_NUM_AKM_SUITES]);
+
return NL_SKIP;
}
@@ -1164,6 +1196,9 @@
if (info->update_ft_ies_supported)
drv->capa.flags |= WPA_DRIVER_FLAGS_UPDATE_FT_IES;
+ if (!drv->capa.max_num_akms)
+ drv->capa.max_num_akms = 1;
+
return 0;
}
@@ -1326,6 +1361,22 @@
drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_AP;
if (check_feature(QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON, &info))
drv->capa.flags |= WPA_DRIVER_FLAGS_OCE_STA_CFON;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_STA, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_LTF_AP, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_LTF_AP;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_STA, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_STA;
+ if (check_feature(QCA_WLAN_VENDOR_FEATURE_SECURE_RTT_AP, &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_SEC_RTT_AP;
+ if (check_feature(
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_STA,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
+ if (check_feature(
+ QCA_WLAN_VENDOR_FEATURE_PROT_RANGE_NEGO_AND_MEASURE_AP,
+ &info))
+ drv->capa.flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
os_free(info.flags);
}
@@ -2474,7 +2525,8 @@
}
-static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes,
+static void nl80211_dump_chan_list(struct wpa_driver_nl80211_data *drv,
+ struct hostapd_hw_modes *modes,
u16 num_modes)
{
int i;
@@ -2492,6 +2544,9 @@
for (j = 0; j < mode->num_channels; j++) {
struct hostapd_channel_data *chan = &mode->channels[j];
+ if (chan->freq >= 5925 && chan->freq <= 7125 &&
+ !(chan->flag & HOSTAPD_CHAN_DISABLED))
+ drv->uses_6ghz = true;
res = os_snprintf(pos, end - pos, " %d%s%s%s",
chan->freq,
(chan->flag & HOSTAPD_CHAN_DISABLED) ?
@@ -2563,7 +2618,7 @@
modes = wpa_driver_nl80211_postprocess_modes(result.modes,
num_modes);
- nl80211_dump_chan_list(modes, *num_modes);
+ nl80211_dump_chan_list(drv, modes, *num_modes);
return modes;
}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 01c7ab4..50fe438 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -178,6 +178,11 @@
C2S(NL80211_CMD_COLOR_CHANGE_COMPLETED)
C2S(NL80211_CMD_SET_FILS_AAD)
C2S(NL80211_CMD_ASSOC_COMEBACK)
+ C2S(NL80211_CMD_ADD_LINK)
+ C2S(NL80211_CMD_REMOVE_LINK)
+ C2S(NL80211_CMD_ADD_LINK_STA)
+ C2S(NL80211_CMD_MODIFY_LINK_STA)
+ C2S(NL80211_CMD_REMOVE_LINK_STA)
C2S(__NL80211_CMD_AFTER_LAST)
}
#undef C2S
@@ -414,11 +419,147 @@
}
}
+
+static void
+nl80211_parse_qca_vendor_mlo_link_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *mlo_links)
+{
+ struct nlattr *link;
+ int rem_links;
+
+ nla_for_each_nested(link, mlo_links, rem_links) {
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX + 1];
+ int link_id;
+
+ nla_parse(tb,QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAX, nla_data(link),
+ nla_len(link), NULL);
+
+ if (!tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID])
+ continue;
+
+ link_id = nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ mlo->valid_links |= BIT(link_id);
+ os_memcpy(mlo->links[link_id].addr,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_MAC_ADDR]),
+ ETH_ALEN);
+ os_memcpy(mlo->links[link_id].bssid,
+ nla_data(tb[QCA_WLAN_VENDOR_ATTR_MLO_LINK_BSSID]),
+ ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+ " bssid " MACSTR,
+ link_id, MAC2STR(mlo->links[link_id].addr),
+ MAC2STR(mlo->links[link_id].bssid));
+ }
+}
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
+static void nl80211_parse_mlo_link_info(struct driver_sta_mlo_info *mlo,
+ struct nlattr *mlo_links)
+{
+ struct nlattr *link;
+ int rem_links;
+
+ nla_for_each_nested(link, mlo_links, rem_links) {
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ int link_id;
+
+ nla_parse(tb, NL80211_ATTR_MAX, nla_data(link), nla_len(link),
+ NULL);
+
+ if (!tb[NL80211_ATTR_MLO_LINK_ID] || !tb[NL80211_ATTR_MAC] ||
+ !tb[NL80211_ATTR_BSSID])
+ continue;
+
+ link_id = nla_get_u8(tb[NL80211_ATTR_MLO_LINK_ID]);
+ if (link_id >= MAX_NUM_MLD_LINKS)
+ continue;
+
+ mlo->valid_links |= BIT(link_id);
+ os_memcpy(mlo->links[link_id].addr,
+ nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+ os_memcpy(mlo->links[link_id].bssid,
+ nla_data(tb[NL80211_ATTR_BSSID]), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: MLO link[%u] addr " MACSTR
+ " bssid " MACSTR,
+ link_id, MAC2STR(mlo->links[link_id].addr),
+ MAC2STR(mlo->links[link_id].bssid));
+ }
+}
+
+
+static int nl80211_get_assoc_link_id(const u8 *data, u8 len)
+{
+ if (!(data[0] & BASIC_MULTI_LINK_CTRL0_PRES_LINK_ID))
+ return -1;
+
+#define BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX \
+ (2 + /* Multi-Link Control field */ \
+ 1 + /* Common Info Length field (Basic) */ \
+ ETH_ALEN) /* MLD MAC Address field (Basic) */
+ if (len <= BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX)
+ return -1;
+
+ return data[BASIC_ML_IE_COMMON_INFO_LINK_ID_IDX] & 0x0F;
+}
+
+
+static void nl80211_parse_mlo_info(struct wpa_driver_nl80211_data *drv,
+ bool qca_roam_auth,
+ struct nlattr *addr,
+ struct nlattr *mlo_links,
+ struct nlattr *resp_ie)
+{
+ const u8 *ml_ie;
+ struct driver_sta_mlo_info *mlo = &drv->sta_mlo_info;
+
+ if (!addr || !mlo_links || !resp_ie)
+ return;
+
+ ml_ie = get_ml_ie(nla_data(resp_ie), nla_len(resp_ie),
+ MULTI_LINK_CONTROL_TYPE_BASIC);
+ if (!ml_ie)
+ return;
+
+ drv->mlo_assoc_link_id = nl80211_get_assoc_link_id(&ml_ie[3],
+ ml_ie[1] - 1);
+ if (drv->mlo_assoc_link_id < 0 ||
+ drv->mlo_assoc_link_id >= MAX_NUM_MLD_LINKS)
+ return;
+
+ os_memcpy(mlo->ap_mld_addr, nla_data(addr), ETH_ALEN);
+ wpa_printf(MSG_DEBUG, "nl80211: AP MLD MAC Address " MACSTR,
+ MAC2STR(mlo->ap_mld_addr));
+
+ if (!qca_roam_auth)
+ nl80211_parse_mlo_link_info(mlo, mlo_links);
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ if (qca_roam_auth)
+ nl80211_parse_qca_vendor_mlo_link_info(mlo, mlo_links);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
+ if (!(mlo->valid_links & BIT(drv->mlo_assoc_link_id))) {
+ wpa_printf(MSG_ERROR, "nl80211: Invalid MLO assoc link ID %d",
+ drv->mlo_assoc_link_id);
+ mlo->valid_links = 0;
+ return;
+ }
+
+ os_memcpy(drv->bssid, mlo->links[drv->mlo_assoc_link_id].bssid,
+ ETH_ALEN);
+ os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+}
+
+
static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
- enum nl80211_commands cmd, struct nlattr *status,
+ enum nl80211_commands cmd, bool qca_roam_auth,
+ struct nlattr *status,
struct nlattr *addr, struct nlattr *req_ie,
struct nlattr *resp_ie,
struct nlattr *timed_out,
@@ -430,7 +571,8 @@
struct nlattr *subnet_status,
struct nlattr *fils_erp_next_seq_num,
struct nlattr *fils_pmk,
- struct nlattr *fils_pmkid)
+ struct nlattr *fils_pmkid,
+ struct nlattr *mlo_links)
{
union wpa_event_data event;
const u8 *ssid = NULL;
@@ -522,7 +664,9 @@
}
drv->associated = 1;
- if (addr) {
+ drv->sta_mlo_info.valid_links = 0;
+ nl80211_parse_mlo_info(drv, qca_roam_auth, addr, mlo_links, resp_ie);
+ if (!drv->sta_mlo_info.valid_links && addr) {
os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
}
@@ -690,10 +834,10 @@
static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
- struct nlattr *ifindex, struct nlattr *freq,
- struct nlattr *type, struct nlattr *bw,
- struct nlattr *cf1, struct nlattr *cf2,
- int finished)
+ struct nlattr *ifindex, struct nlattr *link,
+ struct nlattr *freq, struct nlattr *type,
+ struct nlattr *bw, struct nlattr *cf1,
+ struct nlattr *cf2, int finished)
{
struct i802_bss *bss;
union wpa_event_data data;
@@ -757,6 +901,25 @@
if (finished)
bss->freq = data.ch_switch.freq;
+
+ if (link) {
+ u8 link_id = nla_get_u8(link);
+
+ if (link_id < MAX_NUM_MLD_LINKS &&
+ drv->sta_mlo_info.valid_links & BIT(link_id)) {
+ data.ch_switch.link_id = link_id;
+ drv->sta_mlo_info.links[link_id].freq =
+ data.ch_switch.freq;
+ wpa_supplicant_event(
+ bss->ctx,
+ finished ? EVENT_LINK_CH_SWITCH :
+ EVENT_LINK_CH_SWITCH_STARTED, &data);
+ }
+
+ if (link_id != drv->mlo_assoc_link_id)
+ return;
+ }
+
drv->assoc_freq = data.ch_switch.freq;
wpa_supplicant_event(bss->ctx, finished ?
@@ -1083,6 +1246,7 @@
struct nlattr *wmm, struct nlattr *req_ie)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
+ u16 stype = 0, auth_type = 0;
const u8 *data;
size_t len;
@@ -1112,11 +1276,31 @@
nl80211_command_to_string(cmd), bss->ifname,
MAC2STR(bss->addr), MAC2STR(data + 4),
MAC2STR(data + 4 + ETH_ALEN));
- if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
- os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
- (is_zero_ether_addr(bss->rand_addr) ||
- os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
- os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+
+ /* PASN Authentication frame can be received with a different source MAC
+ * address. Allow NL80211_CMD_FRAME event with foreign addresses also.
+ */
+ if (cmd == NL80211_CMD_FRAME && len >= 24) {
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+ }
+
+ if (cmd == NL80211_CMD_FRAME && stype == WLAN_FC_STYPE_AUTH &&
+ auth_type == host_to_le16(WLAN_AUTH_PASN)) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: %s: Allow PASN frame for foreign address",
+ bss->ifname);
+ } else if (cmd != NL80211_CMD_FRAME_TX_STATUS &&
+ !(data[4] & 0x01) &&
+ os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+ (is_zero_ether_addr(bss->rand_addr) ||
+ os_memcmp(bss->rand_addr, data + 4, ETH_ALEN) != 0) &&
+ os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
"for foreign address", bss->ifname);
return;
@@ -1493,7 +1677,7 @@
* nl80211_get_link_signal() and nl80211_get_link_noise() set default
* values in case querying the driver fails.
*/
- res = nl80211_get_link_signal(drv, &ed.signal_change);
+ res = nl80211_get_link_signal(drv, drv->bssid, &ed.signal_change);
if (res == 0) {
wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm txrate: %d",
ed.signal_change.current_signal,
@@ -2095,7 +2279,7 @@
bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
wpa_printf(MSG_DEBUG, " * roam BSSID " MACSTR, MAC2STR(bssid));
- mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+ mlme_event_connect(drv, NL80211_CMD_ROAM, true, NULL,
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
@@ -2107,7 +2291,8 @@
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_SUBNET_STATUS],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM],
tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMK],
- tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID]);
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PMKID],
+ tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MLO_LINKS]);
}
@@ -2118,7 +2303,6 @@
if (!drv->roam_indication_done) {
wpa_printf(MSG_DEBUG,
"nl80211: Pending roam indication, delay processing roam+auth vendor event");
- os_get_reltime(&drv->pending_roam_ind_time);
os_free(drv->pending_roam_data);
drv->pending_roam_data = os_memdup(data, len);
@@ -2374,6 +2558,82 @@
wpa_supplicant_event(drv->ctx, EVENT_P2P_LO_STOP, &event);
}
+
+#ifdef CONFIG_PASN
+
+static void qca_nl80211_pasn_auth(struct wpa_driver_nl80211_data *drv,
+ u8 *data, size_t len)
+{
+ int ret = -EINVAL;
+ struct nlattr *attr;
+ struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+ struct nlattr *cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX + 1];
+ unsigned int n_peers = 0, idx = 0;
+ int rem_conf;
+ enum qca_wlan_vendor_pasn_action action;
+ union wpa_event_data event;
+
+ if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_PASN_MAX,
+ (struct nlattr *) data, len, NULL) ||
+ !tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS] ||
+ !tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]) {
+ return;
+ }
+
+ os_memset(&event, 0, sizeof(event));
+ action = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_PASN_ACTION]);
+ switch (action) {
+ case QCA_WLAN_VENDOR_PASN_ACTION_AUTH:
+ event.pasn_auth.action = PASN_ACTION_AUTH;
+ break;
+ case QCA_WLAN_VENDOR_PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT:
+ event.pasn_auth.action =
+ PASN_ACTION_DELETE_SECURE_RANGING_CONTEXT;
+ break;
+ default:
+ return;
+ }
+
+ nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS], rem_conf)
+ n_peers++;
+
+ if (n_peers > WPAS_MAX_PASN_PEERS) {
+ wpa_printf(MSG_DEBUG, "nl80211: PASN auth: too many peers (%d)",
+ n_peers);
+ return;
+ }
+
+ nla_for_each_nested(attr, tb[QCA_WLAN_VENDOR_ATTR_PASN_PEERS],
+ rem_conf) {
+ struct nlattr *nl_src, *nl_peer;
+
+ ret = nla_parse_nested(cfg, QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAX,
+ attr, NULL);
+ if (ret)
+ return;
+ nl_src = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_SRC_ADDR];
+ nl_peer = cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_MAC_ADDR];
+ if (nl_src)
+ os_memcpy(event.pasn_auth.peer[idx].own_addr, nl_src,
+ ETH_ALEN);
+ if (nl_peer)
+ os_memcpy(event.pasn_auth.peer[idx].peer_addr, nl_peer,
+ ETH_ALEN);
+ if (cfg[QCA_WLAN_VENDOR_ATTR_PASN_PEER_LTF_KEYSEED_REQUIRED])
+ event.pasn_auth.peer[idx].ltf_keyseed_required = true;
+ idx++;
+ }
+ event.pasn_auth.num_peers = n_peers;
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: PASN auth action: %u, num_bssids: %d",
+ event.pasn_auth.action,
+ event.pasn_auth.num_peers);
+ wpa_supplicant_event(drv->ctx, EVENT_PASN_AUTH, &event);
+}
+
+#endif /* CONFIG_PASN */
+
#endif /* CONFIG_DRIVER_NL80211_QCA */
@@ -2410,6 +2670,11 @@
case QCA_NL80211_VENDOR_SUBCMD_P2P_LISTEN_OFFLOAD_STOP:
qca_nl80211_p2p_lo_stop_event(drv, data, len);
break;
+#ifdef CONFIG_PASN
+ case QCA_NL80211_VENDOR_SUBCMD_PASN:
+ qca_nl80211_pasn_auth(drv, data, len);
+ break;
+#endif /* CONFIG_PASN */
#endif /* CONFIG_DRIVER_NL80211_QCA */
default:
wpa_printf(MSG_DEBUG,
@@ -2933,8 +3198,8 @@
data.bss_color_collision.bitmap =
nla_get_u64(tb[NL80211_ATTR_OBSS_COLOR_BITMAP]);
- wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08lx",
- data.bss_color_collision.bitmap);
+ wpa_printf(MSG_DEBUG, "nl80211: BSS color collision - bitmap %08llx",
+ (long long unsigned int) data.bss_color_collision.bitmap);
wpa_supplicant_event(drv->ctx, EVENT_BSS_COLOR_COLLISION, &data);
}
@@ -2985,17 +3250,10 @@
if (cmd == NL80211_CMD_ROAM &&
(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
if (drv->pending_roam_data) {
- struct os_reltime now, age;
-
- os_get_reltime(&now);
- os_reltime_sub(&now, &drv->pending_roam_ind_time, &age);
- if (age.sec == 0 && age.usec < 100000) {
- wpa_printf(MSG_DEBUG,
- "nl80211: Process pending roam+auth vendor event");
- qca_nl80211_key_mgmt_auth(
- drv, drv->pending_roam_data,
- drv->pending_roam_data_len);
- }
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Process pending roam+auth vendor event");
+ qca_nl80211_key_mgmt_auth(drv, drv->pending_roam_data,
+ drv->pending_roam_data_len);
os_free(drv->pending_roam_data);
drv->pending_roam_data = NULL;
return;
@@ -3097,7 +3355,7 @@
break;
case NL80211_CMD_CONNECT:
case NL80211_CMD_ROAM:
- mlme_event_connect(drv, cmd,
+ mlme_event_connect(drv, cmd, false,
tb[NL80211_ATTR_STATUS_CODE],
tb[NL80211_ATTR_MAC],
tb[NL80211_ATTR_REQ_IE],
@@ -3109,11 +3367,13 @@
NULL,
tb[NL80211_ATTR_FILS_ERP_NEXT_SEQ_NUM],
tb[NL80211_ATTR_PMK],
- tb[NL80211_ATTR_PMKID]);
+ tb[NL80211_ATTR_PMKID],
+ tb[NL80211_ATTR_MLO_LINKS]);
break;
case NL80211_CMD_CH_SWITCH_STARTED_NOTIFY:
mlme_event_ch_switch(drv,
tb[NL80211_ATTR_IFINDEX],
+ tb[NL80211_ATTR_MLO_LINK_ID],
tb[NL80211_ATTR_WIPHY_FREQ],
tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
tb[NL80211_ATTR_CHANNEL_WIDTH],
@@ -3124,6 +3384,7 @@
case NL80211_CMD_CH_SWITCH_NOTIFY:
mlme_event_ch_switch(drv,
tb[NL80211_ATTR_IFINDEX],
+ tb[NL80211_ATTR_MLO_LINK_ID],
tb[NL80211_ATTR_WIPHY_FREQ],
tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
tb[NL80211_ATTR_CHANNEL_WIDTH],
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index c31ed6e..d54dd3a 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -412,7 +412,7 @@
drv->scan_state = SCAN_REQUESTED;
/* Not all drivers generate "scan completed" wireless event, so try to
* read results after a timeout. */
- timeout = 10;
+ timeout = drv->uses_6ghz ? 15 : 10;
if (drv->scan_complete_events) {
/*
* The driver seems to deliver events to notify when scan is
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 0568a79..ffb7c57 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -324,6 +324,17 @@
*/
/**
+ * DOC: Multi-Link Operation
+ *
+ * In Multi-Link Operation, a connection between to MLDs utilizes multiple
+ * links. To use this in nl80211, various commands and responses now need
+ * to or will include the new %NL80211_ATTR_MLO_LINKS attribute.
+ * Additionally, various commands that need to operate on a specific link
+ * now need to be given the %NL80211_ATTR_MLO_LINK_ID attribute, e.g. to
+ * use %NL80211_CMD_START_AP or similar functions.
+ */
+
+/**
* enum nl80211_commands - supported nl80211 commands
*
* @NL80211_CMD_UNSPEC: unspecified command to catch errors
@@ -753,6 +764,13 @@
* %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
* counters which will be updated to the current value. This attribute
* is used during CSA period.
+ * For TX on an MLD, the frequency can be omitted and the link ID be
+ * specified, or if transmitting to a known peer MLD (with MLD addresses
+ * in the frame) both can be omitted and the link will be selected by
+ * lower layers.
+ * For RX notification, %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ * indicate the frame RX timestamp and %NL80211_ATTR_TX_HW_TIMESTAMP may
+ * be included to indicate the ack TX timestamp.
* @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
* command may be used with the corresponding cookie to cancel the wait
* time if it is known that it is no longer necessary. This command is
@@ -763,7 +781,9 @@
* transmitted with %NL80211_CMD_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.
+ * the frame. %NL80211_ATTR_TX_HW_TIMESTAMP may be included to indicate the
+ * tx timestamp and %NL80211_ATTR_RX_HW_TIMESTAMP may be included to
+ * indicate the ack RX timestamp.
* @NL80211_CMD_ACTION_TX_STATUS: Alias for @NL80211_CMD_FRAME_TX_STATUS for
* backward compatibility.
*
@@ -1108,6 +1128,12 @@
* has been received. %NL80211_ATTR_FRAME is used to specify the
* frame contents. The frame is the raw EAPoL data, without ethernet or
* 802.11 headers.
+ * For an MLD transmitter, the %NL80211_ATTR_MLO_LINK_ID may be given and
+ * its effect will depend on the destination: If the destination is known
+ * to be an MLD, this will be used as a hint to select the link to transmit
+ * the frame on. If the destination is not an MLD, this will select both
+ * the link to transmit on and the source address will be set to the link
+ * address of that link.
* When used as an event indication %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
* %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT and %NL80211_ATTR_MAC are added
* indicating the protocol type of the received frame; whether the frame
@@ -1237,6 +1263,16 @@
* to describe the BSSID address of the AP and %NL80211_ATTR_TIMEOUT to
* specify the timeout value.
*
+ * @NL80211_CMD_ADD_LINK: Add a new link to an interface. The
+ * %NL80211_ATTR_MLO_LINK_ID attribute is used for the new link.
+ * @NL80211_CMD_REMOVE_LINK: Remove a link from an interface. This may come
+ * without %NL80211_ATTR_MLO_LINK_ID as an easy way to remove all links
+ * in preparation for e.g. roaming to a regular (non-MLO) AP.
+ *
+ * @NL80211_CMD_ADD_LINK_STA: Add a link to an MLD station
+ * @NL80211_CMD_MODIFY_LINK_STA: Modify a link of an MLD station
+ * @NL80211_CMD_REMOVE_LINK_STA: Remove a link of an MLD station
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1481,6 +1517,13 @@
NL80211_CMD_ASSOC_COMEBACK,
+ NL80211_CMD_ADD_LINK,
+ NL80211_CMD_REMOVE_LINK,
+
+ NL80211_CMD_ADD_LINK_STA,
+ NL80211_CMD_MODIFY_LINK_STA,
+ NL80211_CMD_REMOVE_LINK_STA,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -2340,8 +2383,10 @@
*
* @NL80211_ATTR_IFTYPE_EXT_CAPA: Nested attribute of the following attributes:
* %NL80211_ATTR_IFTYPE, %NL80211_ATTR_EXT_CAPA,
- * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities per
- * interface type.
+ * %NL80211_ATTR_EXT_CAPA_MASK, to specify the extended capabilities and
+ * other interface-type specific capabilities per interface type. For MLO,
+ * %NL80211_ATTR_EML_CAPABILITY and %NL80211_ATTR_MLD_CAPA_AND_OPS are
+ * present.
*
* @NL80211_ATTR_MU_MIMO_GROUP_DATA: array of 24 bytes that defines a MU-MIMO
* groupID for monitor mode.
@@ -2663,6 +2708,39 @@
* association request when used with NL80211_CMD_NEW_STATION). Can be set
* only if %NL80211_STA_FLAG_WME is set.
*
+ * @NL80211_ATTR_MLO_LINK_ID: A (u8) link ID for use with MLO, to be used with
+ * various commands that need a link ID to operate.
+ * @NL80211_ATTR_MLO_LINKS: A nested array of links, each containing some
+ * per-link information and a link ID.
+ * @NL80211_ATTR_MLD_ADDR: An MLD address, used with various commands such as
+ * authenticate/associate.
+ *
+ * @NL80211_ATTR_MLO_SUPPORT: Flag attribute to indicate user space supports MLO
+ * connection. Used with %NL80211_CMD_CONNECT. If this attribute is not
+ * included in NL80211_CMD_CONNECT drivers must not perform MLO connection.
+ *
+ * @NL80211_ATTR_MAX_NUM_AKM_SUITES: U16 attribute. Indicates maximum number of
+ * AKM suites allowed for %NL80211_CMD_CONNECT, %NL80211_CMD_ASSOCIATE and
+ * %NL80211_CMD_START_AP in %NL80211_CMD_GET_WIPHY response. If this
+ * attribute is not present userspace shall consider maximum number of AKM
+ * suites allowed as %NL80211_MAX_NR_AKM_SUITES which is the legacy maximum
+ * number prior to the introduction of this attribute.
+ *
+ * @NL80211_ATTR_EML_CAPABILITY: EML Capability information (u16)
+ * @NL80211_ATTR_MLD_CAPA_AND_OPS: MLD Capabilities and Operations (u16)
+ *
+ * @NL80211_ATTR_TX_HW_TIMESTAMP: Hardware timestamp for TX operation in
+ * nanoseconds (u64). This is the device clock timestamp so it will
+ * probably reset when the device is stopped or the firmware is reset.
+ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the frame TX
+ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ * the ack TX timestamp.
+ * @NL80211_ATTR_RX_HW_TIMESTAMP: Hardware timestamp for RX operation in
+ * nanoseconds (u64). This is the device clock timestamp so it will
+ * probably reset when the device is stopped or the firmware is reset.
+ * When used with %NL80211_CMD_FRAME_TX_STATUS, indicates the ack RX
+ * timestamp. When used with %NL80211_CMD_FRAME RX notification, indicates
+ * the incoming frame RX timestamp.
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3175,6 +3253,22 @@
NL80211_ATTR_EHT_CAPABILITY,
+ NL80211_ATTR_DISABLE_EHT,
+
+ NL80211_ATTR_MLO_LINKS,
+ NL80211_ATTR_MLO_LINK_ID,
+ NL80211_ATTR_MLD_ADDR,
+
+ NL80211_ATTR_MLO_SUPPORT,
+
+ NL80211_ATTR_MAX_NUM_AKM_SUITES,
+
+ NL80211_ATTR_EML_CAPABILITY,
+ NL80211_ATTR_MLD_CAPA_AND_OPS,
+
+ NL80211_ATTR_TX_HW_TIMESTAMP,
+ NL80211_ATTR_RX_HW_TIMESTAMP,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3229,6 +3323,11 @@
#define NL80211_HE_MIN_CAPABILITY_LEN 16
#define NL80211_HE_MAX_CAPABILITY_LEN 54
#define NL80211_MAX_NR_CIPHER_SUITES 5
+
+/*
+ * NL80211_MAX_NR_AKM_SUITES is obsolete when %NL80211_ATTR_MAX_NUM_AKM_SUITES
+ * present in %NL80211_CMD_GET_WIPHY response.
+ */
#define NL80211_MAX_NR_AKM_SUITES 2
#define NL80211_EHT_MIN_CAPABILITY_LEN 13
#define NL80211_EHT_MAX_CAPABILITY_LEN 51
@@ -4851,6 +4950,7 @@
* 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_MLO_LINK_ID: MLO link ID of the BSS (u8).
* @__NL80211_BSS_AFTER_LAST: internal
* @NL80211_BSS_MAX: highest BSS attribute
*/
@@ -4876,6 +4976,7 @@
NL80211_BSS_PARENT_BSSID,
NL80211_BSS_CHAIN_SIGNAL,
NL80211_BSS_FREQUENCY_OFFSET,
+ NL80211_BSS_MLO_LINK_ID,
/* keep last */
__NL80211_BSS_AFTER_LAST,
@@ -5872,7 +5973,7 @@
* @NL80211_FEATURE_INACTIVITY_TIMER: This driver takes care of freeing up
* the connected inactive stations in AP mode.
* @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
- * to work properly to suppport receiving regulatory hints from
+ * to work properly to support receiving regulatory hints from
* cellular base stations.
* @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
* here to reserve the value for API/ABI compatibility)
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index 768fc10..27bdac3 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -2428,5 +2428,6 @@
bool is_p2p_allow_6ghz(struct p2p_data *p2p);
void set_p2p_allow_6ghz(struct p2p_data *p2p, bool value);
int p2p_remove_6ghz_channels(struct weighted_pcl *pref_freq_list, int size);
+int p2p_channel_to_freq(int op_class, int channel);
#endif /* P2P_H */
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index bebc5e2..d8dc6f7 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -691,7 +691,6 @@
/* p2p_utils.c */
int p2p_random(char *buf, size_t len);
-int p2p_channel_to_freq(int op_class, int channel);
int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
void p2p_channels_intersect(const struct p2p_channels *a,
const struct p2p_channels *b,
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index 17a79f9..21f8b5b 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -302,7 +302,8 @@
#ifdef CONFIG_IEEE80211R
sm->xxkey_len = 0;
#ifdef CONFIG_SAE
- if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE &&
+ if ((sm->key_mgmt == WPA_KEY_MGMT_FT_SAE ||
+ sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) &&
sm->pmk_len == PMK_LEN) {
/* Need to allow FT key derivation to proceed with
* PMK from SAE being used as the XXKey in cases where
@@ -585,6 +586,7 @@
static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
{
+ int ret;
const u8 *z = NULL;
size_t z_len = 0, kdk_len;
int akmp;
@@ -618,11 +620,23 @@
else
kdk_len = 0;
- return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
- sm->own_addr, sm->bssid, sm->snonce,
- key->key_nonce, ptk, akmp,
- sm->pairwise_cipher, z, z_len,
- kdk_len);
+ ret = wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+ sm->own_addr, sm->bssid, sm->snonce,
+ key->key_nonce, ptk, akmp,
+ sm->pairwise_cipher, z, z_len,
+ kdk_len);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "WPA: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->secure_ltf &&
+ ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+ ret = wpa_ltf_keyseed(ptk, akmp, sm->pairwise_cipher);
+#endif /* CONFIG_PASN */
+
+ return ret;
}
@@ -976,6 +990,20 @@
return -1;
}
+#ifdef CONFIG_PASN
+ if (sm->secure_ltf &&
+ ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_sm_set_ltf_keyseed(sm, sm->own_addr, sm->bssid,
+ sm->ptk.ltf_keyseed_len,
+ sm->ptk.ltf_keyseed) < 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to set LTF keyseed to the driver (keylen=%zu bssid="
+ MACSTR ")", sm->ptk.ltf_keyseed_len,
+ MAC2STR(sm->bssid));
+ return -1;
+ }
+#endif /* CONFIG_PASN */
+
wpa_sm_store_ptk(sm, sm->bssid, sm->pairwise_cipher,
sm->dot11RSNAConfigPMKLifetime, &sm->ptk);
@@ -4493,6 +4521,15 @@
goto fail;
}
+#ifdef CONFIG_PASN
+ if (sm->secure_ltf &&
+ ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_ltf_keyseed(&sm->ptk, sm->key_mgmt, sm->pairwise_cipher)) {
+ wpa_printf(MSG_DEBUG, "FILS: Failed to derive LTF keyseed");
+ goto fail;
+ }
+#endif /* CONFIG_PASN */
+
wpabuf_clear_free(dh_ss);
dh_ss = NULL;
@@ -5322,6 +5359,7 @@
#ifdef CONFIG_PASN
+
void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid, int key_mgmt)
{
@@ -5329,6 +5367,18 @@
bssid, sm->own_addr, NULL,
key_mgmt, 0);
}
+
+
+void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2)
+{
+ if (flags2 & WPA_DRIVER_FLAGS2_SEC_LTF_STA)
+ sm->secure_ltf = 1;
+ if (flags2 & WPA_DRIVER_FLAGS2_SEC_RTT_STA)
+ sm->secure_rtt = 1;
+ if (flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA)
+ sm->prot_range_neg = 1;
+}
+
#endif /* CONFIG_PASN */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 7cca718..411f328 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -93,6 +93,11 @@
void (*transition_disable)(void *ctx, u8 bitmap);
void (*store_ptk)(void *ctx, u8 *addr, int cipher,
u32 life_time, const struct wpa_ptk *ptk);
+#ifdef CONFIG_PASN
+ int (*set_ltf_keyseed)(void *ctx, const u8 *own_addr,
+ const u8 *peer_addr, size_t ltf_keyseed_len,
+ const u8 *ltf_keyseed);
+#endif /* CONFIG_PASN */
};
@@ -582,5 +587,6 @@
void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
const u8 *pmkid, const u8 *bssid, int key_mgmt);
+void wpa_pasn_sm_set_caps(struct wpa_sm *sm, unsigned int flags2);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index 6d4b07b..96d293f 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -41,6 +41,7 @@
int use_sha384 = wpa_key_mgmt_sha384(sm->key_mgmt);
const u8 *mpmk;
size_t mpmk_len, kdk_len;
+ int ret = 0;
if (sm->xxkey_len > 0) {
mpmk = sm->xxkey;
@@ -75,10 +76,22 @@
else
kdk_len = 0;
- 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,
- kdk_len);
+ ret = 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, kdk_len);
+ if (ret) {
+ wpa_printf(MSG_ERROR, "FT: PTK derivation failed");
+ return ret;
+ }
+
+#ifdef CONFIG_PASN
+ if (sm->secure_ltf &&
+ ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF))
+ ret = wpa_ltf_keyseed(ptk, sm->key_mgmt, sm->pairwise_cipher);
+#endif /* CONFIG_PASN */
+
+ return ret;
}
@@ -257,6 +270,8 @@
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+ else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY)
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
#ifdef CONFIG_FILS
else if (sm->key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256)
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_FILS_SHA256);
@@ -686,6 +701,15 @@
kdk_len) < 0)
return -1;
+#ifdef CONFIG_PASN
+ if (sm->secure_ltf &&
+ ieee802_11_rsnx_capab(sm->ap_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF) &&
+ wpa_ltf_keyseed(&sm->ptk, sm->key_mgmt, sm->pairwise_cipher)) {
+ wpa_printf(MSG_DEBUG, "FT: Failed to derive LTF keyseed");
+ return -1;
+ }
+#endif /* CONFIG_PASN */
+
if (wpa_key_mgmt_fils(sm->key_mgmt)) {
kck = sm->ptk.kck2;
kck_len = sm->ptk.kck2_len;
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index fabd6cb..3811c3b 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -483,6 +483,18 @@
ptk);
}
+#ifdef CONFIG_PASN
+static inline int wpa_sm_set_ltf_keyseed(struct wpa_sm *sm, const u8 *own_addr,
+ const u8 *peer_addr,
+ size_t ltf_keyseed_len,
+ const u8 *ltf_keyseed)
+{
+ WPA_ASSERT(sm->ctx->set_ltf_keyseed);
+ return sm->ctx->set_ltf_keyseed(sm->ctx->ctx, own_addr, peer_addr,
+ ltf_keyseed_len, ltf_keyseed);
+}
+#endif /* CONFIG_PASN */
+
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
u8 *msg, size_t msg_len, u8 *key_mic);
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index 3ba722f..c4e660f 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -191,8 +191,12 @@
#ifdef CONFIG_SAE
} else if (key_mgmt == WPA_KEY_MGMT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE);
+ } else if (key_mgmt == WPA_KEY_MGMT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_SAE_EXT_KEY);
} else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
+ } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE_EXT_KEY) {
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE_EXT_KEY);
#endif /* CONFIG_SAE */
} else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
diff --git a/src/utils/crc32.c b/src/utils/crc32.c
index 12d9e2a..3712549 100644
--- a/src/utils/crc32.c
+++ b/src/utils/crc32.c
@@ -72,7 +72,7 @@
};
-u32 crc32(const u8 *frame, size_t frame_len)
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len)
{
size_t i;
u32 crc;
diff --git a/src/utils/crc32.h b/src/utils/crc32.h
index dc31399..71a19dc 100644
--- a/src/utils/crc32.h
+++ b/src/utils/crc32.h
@@ -9,6 +9,6 @@
#ifndef CRC32_H
#define CRC32_H
-u32 crc32(const u8 *frame, size_t frame_len);
+u32 ieee80211_crc32(const u8 *frame, size_t frame_len);
#endif /* CRC32_H */
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
index 92a3590..a971f72 100644
--- a/src/utils/ip_addr.c
+++ b/src/utils/ip_addr.c
@@ -51,3 +51,22 @@
return -1;
}
+
+
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+ const struct hostapd_ip_addr *b)
+{
+ if (a->af != b->af)
+ return false;
+
+ if (a->af == AF_INET && a->u.v4.s_addr == b->u.v4.s_addr)
+ return true;
+
+#ifdef CONFIG_IPV6
+ if (a->af == AF_INET6 &&
+ os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) == 0)
+ return true;
+#endif /* CONFIG_IPV6 */
+
+ return false;
+}
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
index 0670411..1d35e0b 100644
--- a/src/utils/ip_addr.h
+++ b/src/utils/ip_addr.h
@@ -23,5 +23,7 @@
const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
size_t buflen);
int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+bool hostapd_ip_equal(const struct hostapd_ip_addr *a,
+ const struct hostapd_ip_addr *b);
#endif /* IP_ADDR_H */