OBJS += src/common/sae.c
ifdef CONFIG_SAE_PK
L_CFLAGS += -DCONFIG_SAE_PK
-NEED_AES_SIV=y
NEED_BASE64=y
OBJS += src/common/sae_pk.c
endif
+NEED_AES_SIV=y
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
OBJS += ../src/common/sae.o
ifdef CONFIG_SAE_PK
CFLAGS += -DCONFIG_SAE_PK
-NEED_AES_SIV=y
NEED_BASE64=y
OBJS += ../src/common/sae_pk.o
endif
+NEED_AES_SIV=y
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
bss->sae_confirm_immediate = atoi(pos);
} else if (os_strcmp(buf, "sae_pwe") == 0) {
bss->sae_pwe = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pw_id_num") == 0) {
+ bss->sae_pw_id_num = atoi(pos);
+ } else if (os_strcmp(buf, "sae_pw_id_key") == 0) {
+ if (parse_wpabuf_hex(line, buf, &bss->sae_pw_id_key, pos))
+ return 1;
} else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
int val = atoi(pos);
if (val < 0 || val > 255) {
# regardless of the sae_pwe parameter value.
#sae_pwe=0
+# Changing SAE password identifiers
+# AP can send a set of unique SAE password identifiers to allow the long term
+# password identifier to be changed for over the air exchanges to avoid tracking
+# of devices using password identifiers.
+#
+# Key for generating and interpreting encrypted password identifiers. This must
+# be same for each AP in the network. 32 octets as a hexdump
+#sae_pw_id_key=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
+#
+# How many over the air password identifier values to provide to a STA each time
+# SAE is used.
+#sae_pw_id_num=2
+
# FILS Cache Identifier (16-bit value in hexdump format)
#fils_cache_id=0011
os_free(conf->pasn_groups);
#endif /* CONFIG_PASN */
+ wpabuf_clear_free(conf->sae_pw_id_key);
+
os_free(conf);
}
struct sae_password_entry *sae_passwords;
int sae_password_psk;
int sae_track_password;
+ struct wpabuf *sae_pw_id_key;
+ unsigned int sae_pw_id_num;
char *wowlan_triggers; /* Wake-on-WLAN triggers */
#include "crypto/sha384.h"
#include "crypto/sha512.h"
#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
}
}
+ /* Try to decrypt the received password identifier if no plaintext
+ * identifier match was found. */
+ if (!password && rx_id && rx_id_len > 4 + 4 + AES_BLOCK_SIZE &&
+ hapd->conf->sae_pw_id_key) {
+ u8 *plain, *pos, *counter;
+ size_t plain_len;
+ const u8 *id;
+ size_t id_len;
+
+ plain = os_malloc(rx_id_len);
+ if (!plain)
+ goto fail;
+ if (aes_siv_decrypt(
+ wpabuf_head(hapd->conf->sae_pw_id_key),
+ wpabuf_len(hapd->conf->sae_pw_id_key),
+ rx_id, rx_id_len, 0, NULL, NULL, plain) < 0)
+ goto fail;
+ plain_len = rx_id_len - AES_BLOCK_SIZE;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "SAE: Decrypted password identifier info",
+ plain, plain_len);
+ /* 4 octet date | Password ID | <padding> | 4 octet counter */
+ counter = plain + plain_len - 4;
+ wpa_printf(MSG_DEBUG, "SAE: Generation time %u counter %u",
+ WPA_GET_BE32(plain), WPA_GET_BE32(counter));
+ id = pos = plain + 4;
+ while (pos < counter) {
+ if (*pos == 0x00)
+ break;
+ pos++;
+ }
+ id_len = pos - id;
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "SAE: Decrypted password identifier",
+ id, id_len);
+ for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
+ if (!is_broadcast_ether_addr(pw->peer_addr) &&
+ (!sta ||
+ !ether_addr_equal(pw->peer_addr, sta->addr)))
+ continue;
+ if (!pw->identifier ||
+ os_strlen(pw->identifier) != id_len ||
+ os_memcmp(id, pw->identifier, id_len) != 0)
+ continue;
+ password = pw->password;
+ if (!(hapd->conf->mesh & MESH_ENABLED))
+ pk = pw->pk;
+ if (sta && sta->sae && sta->sae->tmp) {
+ os_free(sta->sae->tmp->dec_pw_id);
+ sta->sae->tmp->dec_pw_id =
+ os_zalloc(id_len + 1);
+ if (sta->sae->tmp->dec_pw_id) {
+ os_memcpy(sta->sae->tmp->dec_pw_id,
+ id, id_len);
+ sta->sae->tmp->dec_pw_id_len = id_len;
+ sta->sae->tmp->pw_id_counter =
+ WPA_GET_BE32(counter);
+ wpa_printf(MSG_DEBUG,
+ "SAE: Bound decrypted password identifier to STA");
+ }
+ }
+ break;
+ }
+ fail:
+ os_free(plain);
+ }
+
found:
if (pw_entry)
*pw_entry = pw;
use_pt = 1;
password = sae_get_password(hapd, sta, rx_id, rx_id_len, &pw, &pt, &pk);
- if (!password || (use_pt && !pt)) {
+ if (!password) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
- if (update && use_pt &&
- sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
- NULL, pk) < 0)
- return NULL;
+ if (use_pt) {
+ struct sae_pt *tmp_pt = NULL;
+ bool failed = false;
+
+ if (!pt && pw) {
+ int groups[2] = { sta->sae->group, 0 };
+
+ wpa_printf(MSG_DEBUG,
+ "SAE: Derive PT for encrypted PW ID");
+ tmp_pt = sae_derive_pt(groups, hapd->conf->ssid.ssid,
+ hapd->conf->ssid.ssid_len,
+ (const u8 *) pw->password,
+ os_strlen(pw->password),
+ rx_id, rx_id_len);
+ if (!tmp_pt) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Could not derive PT");
+ return NULL;
+ }
+ pt = tmp_pt;
+ update = 1;
+ }
+
+ if (!pt) {
+ wpa_printf(MSG_DEBUG, "SAE: No PT available");
+ return NULL;
+ }
+
+ if (update &&
+ sae_prepare_commit_pt(sta->sae, pt, own_addr, sta->addr,
+ NULL, pk) < 0)
+ failed = true;
+
+ sae_deinit_pt(tmp_pt);
+ if (failed)
+ return NULL;
+ }
if (update && !use_pt &&
sae_prepare_commit(own_addr, sta->addr,
sta->sae->pmkid, sta->sae->akmp,
ap_sta_is_mld(hapd, sta), sta->vlan_id);
sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
+ if (sta->sae->tmp) {
+ struct sae_temporary_data *tmp = sta->sae->tmp;
+
+ wpabuf_free(sta->sae_pw_id);
+ sta->sae_pw_id = NULL;
+ if (tmp->dec_pw_id) {
+ sta->sae_pw_id = wpabuf_alloc_copy(
+ tmp->dec_pw_id, tmp->dec_pw_id_len);
+ sta->sae_pw_id_counter = tmp->pw_id_counter;
+ } else if (tmp->pw_id) {
+ sta->sae_pw_id = wpabuf_alloc_copy(
+ tmp->pw_id, tmp->pw_id_len);
+ }
+ }
}
#endif /* CONFIG_IEEE80211BE */
wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
+ if (sta->auth_alg == WLAN_AUTH_SAE)
+ wpa_auth_set_sae_pw_id(sta->wpa_sm, sta->sae_pw_id,
+ sta->sae_pw_id_counter);
wpa_auth_set_rsn_selection(sta->wpa_sm, elems->rsn_selection,
elems->rsn_selection_len);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
forced_memzero(sta->last_tk, WPA_TK_MAX_LEN);
#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_free(sta->sae_pw_id);
+
os_free(sta);
}
* units of 1000 TUs */
u64 last_known_sta_id_timestamp;
+
+ struct wpabuf *sae_pw_id;
+ unsigned int sae_pw_id_counter;
};
#ifdef CONFIG_DPP2
wpabuf_clear_free(sm->dpp_z);
#endif /* CONFIG_DPP2 */
+ wpabuf_free(sm->sae_pw_id);
bin_clear_free(sm, sizeof(*sm));
}
}
+#ifdef CONFIG_SAE
+static u8 * add_sae_pw_ids(struct wpa_state_machine *sm, u8 *pos, u8 *end)
+{
+ static const size_t max_padding = 8;
+ u8 *start = pos, *len;
+ unsigned int i;
+ struct wpa_auth_config *conf = &sm->wpa_auth->conf;
+ u8 *data, *dpos;
+ unsigned int counter;
+ size_t pw_id_len = wpabuf_len(sm->sae_pw_id);
+ struct os_time t;
+ size_t kde_len;
+
+ wpa_printf(MSG_DEBUG, "RSN: Add SAE Password Identifiers KDE (num=%u)",
+ conf->sae_pw_id_num);
+ wpa_hexdump_buf(MSG_DEBUG, "RSN: Real SAE Password Identifier",
+ sm->sae_pw_id);
+ data = os_malloc(pw_id_len + max_padding + 4 + 4);
+ if (!data)
+ return NULL;
+
+ if (end - pos < 2 + RSN_SELECTOR_LEN + 1) {
+ pos = NULL;
+ goto fail;
+ }
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ len = pos++; /* Length to be filled */
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_SAE_PW_IDS);
+ pos += RSN_SELECTOR_LEN;
+
+ *pos++ = 0; /* Flags */
+
+ counter = sm->sae_pw_id_counter + conf->sae_pw_id_num;
+
+ os_get_time(&t);
+
+ WPA_PUT_BE32(data, t.sec);
+ os_memcpy(data + 4, wpabuf_head(sm->sae_pw_id), pw_id_len);
+
+ for (i = 0; i < conf->sae_pw_id_num; i++) {
+ size_t pad_len, dlen, elen;
+
+ pad_len = 1 + os_random() % max_padding;
+ dpos = data + 4 + pw_id_len;
+ os_memset(dpos, 0, pad_len);
+ dpos += pad_len;
+ WPA_PUT_BE32(dpos, counter);
+ dpos += 4;
+ dlen = dpos - data;
+ elen = dlen + AES_BLOCK_SIZE;
+ counter++;
+
+ kde_len = (pos - len - 1) + (1 + elen);
+ if ((size_t) (end - pos) < 1 + elen || kde_len > 255) {
+ wpa_printf(MSG_INFO,
+ "RSN: Not enough room in the buffer for a new SAE Password Identifier - send only %u",
+ i);
+ break;
+ }
+
+ *pos++ = elen;
+ if (aes_siv_encrypt(conf->sae_pw_id_key,
+ sizeof(conf->sae_pw_id_key),
+ data, dlen, 0, NULL, NULL, pos) < 0) {
+ wpa_printf(MSG_INFO,
+ "RSN: Failed to encrypt SAE Password Identifier");
+ pos = NULL;
+ goto fail;
+ }
+ pos += elen;
+ }
+
+ kde_len = pos - len - 1;
+ if (kde_len > 255) {
+ wpa_printf(MSG_INFO,
+ "RSN: SAE Password Identifiers do not fit in a KDE");
+ wpa_hexdump_key(MSG_DEBUG, "RSN: KDE", start, pos - start);
+ pos = NULL;
+ goto fail;
+ }
+
+ *len = kde_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "RSN: SAE Password Identifiers KDE",
+ start, pos - start);
+
+fail:
+ os_free(data);
+ return pos;
+}
+#endif /* CONFIG_SAE */
+
+
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde = NULL, *pos, stub_gtk[32];
#else /* CONFIG_IEEE80211BE */
bool is_mld = false;
#endif /* CONFIG_IEEE80211BE */
+#ifdef CONFIG_SAE
+ bool sae_pw_ids = false;
+#endif /* CONFIG_SAE */
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = false;
if (sm->ssid_protection)
kde_len += 2 + conf->ssid_len;
+#ifdef CONFIG_SAE
+ if (wpa_key_mgmt_sae(sm->wpa_key_mgmt) &&
+ conf->sae_pw_id_num &&
+ sm->sae_pw_id &&
+ ieee802_11_rsnx_capab(sm->rsnxe,
+ WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE)) {
+ kde_len += 2 + 255;
+ sae_pw_ids = true;
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements)
kde_len += wpabuf_len(conf->eapol_m3_elements);
pos += conf->ssid_len;
}
+#ifdef CONFIG_SAE
+ if (sae_pw_ids) {
+ u8 *npos;
+
+ npos = add_sae_pw_ids(sm, pos, kde + kde_len);
+ if (!npos) {
+ wpa_printf(MSG_DEBUG,
+ "RSN: Failed to add SAE Password Identifiers KDE");
+ /* Ignore this since it is not a fatal error for the
+ * Authenticator and the STA can decide whether to
+ * proceed without getting new identifiers. */
+ } else {
+ pos = npos;
+ }
+ }
+#endif /* CONFIG_SAE */
+
#ifdef CONFIG_TESTING_OPTIONS
if (conf->eapol_m3_elements) {
os_memcpy(pos, wpabuf_head(conf->eapol_m3_elements),
return vlan_gsm;
}
#endif /* CONFIG_IEEE80211BE */
+
+
+void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm,
+ const struct wpabuf *pw_id,
+ unsigned int counter)
+{
+ if (sm) {
+ wpabuf_free(sm->sae_pw_id);
+ sm->sae_pw_id = wpabuf_dup(pw_id);
+ sm->sae_pw_id_counter = counter;
+ }
+}
int rsn_override_omit_rsnxe;
bool spp_amsdu;
+
+ unsigned int sae_pw_id_num;
+ u8 sae_pw_id_key[32];
};
typedef enum {
const u8 *mic, size_t mic_len);
struct wpa_group * wpa_select_vlan_wpa_group(struct wpa_group *gsm,
int vlan_id);
+void wpa_auth_set_sae_pw_id(struct wpa_state_machine *sm,
+ const struct wpabuf *pw_id,
+ unsigned int counter);
#endif /* WPA_AUTH_H */
wconf->rsn_override_omit_rsnxe = conf->rsn_override_omit_rsnxe;
wconf->spp_amsdu = conf->spp_amsdu &&
(iface->drv_flags2 & WPA_DRIVER_FLAGS2_SPP_AMSDU);
+
+ if (conf->sae_pw_id_num && conf->sae_pw_id_key &&
+ wpabuf_len(conf->sae_pw_id_key) == sizeof(wconf->sae_pw_id_key)) {
+ wconf->sae_pw_id_num = conf->sae_pw_id_num;
+ os_memcpy(wconf->sae_pw_id_key,
+ wpabuf_head(conf->sae_pw_id_key),
+ wpabuf_len(conf->sae_pw_id_key));
+ }
}
#endif /* CONFIG_IEEE80211BE */
bool ssid_protection;
+
+ struct wpabuf *sae_pw_id;
+ unsigned int sae_pw_id_counter;
};
wpabuf_free(tmp->peer_rejected_groups);
os_free(tmp->pw_id);
os_free(tmp->parsed_pw_id);
+ os_free(tmp->dec_pw_id);
bin_clear_free(tmp, sizeof(*tmp));
sae->tmp = NULL;
}
size_t pw_id_len;
u8 *parsed_pw_id;
size_t parsed_pw_id_len;
+ char *dec_pw_id;
+ size_t dec_pw_id_len;
+ unsigned int pw_id_counter;
int vlan_id;
u8 bssid[ETH_ALEN];
struct wpabuf *own_rejected_groups;