if (!pt)
return NULL;
+ if (identifier) {
+ pt->password_id = wpabuf_alloc_copy(identifier, identifier_len);
+ if (!pt->password_id)
+ goto fail;
+ }
+
#ifdef CONFIG_SAE_PK
os_memcpy(pt->ssid, ssid, ssid_len);
pt->ssid_len = ssid_len;
crypto_ec_point_deinit(pt->ecc_pt, 1);
crypto_bignum_deinit(pt->ffc_pt, 1);
crypto_ec_deinit(pt->ec);
+ wpabuf_free(pt->password_id);
prev = pt;
pt = pt->next;
os_free(prev);
const struct dh_group *dh;
struct crypto_bignum *ffc_pt;
+ struct wpabuf *password_id;
#ifdef CONFIG_SAE_PK
u8 ssid[32];
size_t ssid_len;
return 0;
}
+ if (left > 2 && selector == RSN_KEY_DATA_SAE_PW_IDS) {
+ ie->sae_pw_ids = p;
+ ie->sae_pw_ids_len = left;
+ wpa_hexdump_key(MSG_DEBUG,
+ "RSN: SAE Password Identifiers in EAPOL-Key",
+ pos, dlen);
+ return 0;
+ }
+
if (left >= 1 && selector == WFA_KEY_DATA_IP_ADDR_REQ) {
ie->ip_addr_req = p;
wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
size_t igtk_len;
const u8 *bigtk;
size_t bigtk_len;
+ const u8 *sae_pw_ids;
+ size_t sae_pw_ids_len;
const u8 *mdie;
size_t mdie_len;
const u8 *ftie;
}
+static void wpa_sm_sae_pw_id_change(struct wpa_sm *sm, const u8 *kde,
+ size_t kde_len)
+{
+ const u8 *pos = kde, *end = kde + kde_len;
+ u8 flags;
+ struct wpabuf_array *wa;
+
+ wpa_hexdump(MSG_DEBUG, "RSN: Received SAE Password Identifiers KDE",
+ kde, kde_len);
+
+ if (end - pos < 1)
+ return;
+ flags = *pos++;
+ wpa_printf(MSG_DEBUG, "RSN: SAE Password Identifiers Flags: 0x%x",
+ flags);
+
+ if (!sm->ctx->sae_pw_id_change)
+ return;
+
+ wa = wpabuf_array_alloc();
+ if (!wa)
+ return;
+
+ while (end > pos) {
+ u8 len;
+
+ len = *pos++;
+ if (end - pos < len) {
+ wpa_printf(MSG_INFO,
+ "RSN: Truncated SAE Password Identifier Tuple");
+ wpabuf_array_free(wa);
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "RSN: SAE Password Identifier",
+ pos, len);
+ if (wpabuf_array_add(wa, wpabuf_alloc_copy(pos, len))) {
+ wpabuf_array_free(wa);
+ return;
+ }
+ pos += len;
+ }
+
+ sm->ctx->sae_pw_id_change(sm->ctx->ctx, wa);
+}
+
+
static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
const struct wpa_eapol_key *key,
u16 ver, const u8 *key_data,
if (ie.transition_disable)
wpa_sm_transition_disable(sm, ie.transition_disable[0]);
+ if (ie.sae_pw_ids && wpa_key_mgmt_sae(sm->key_mgmt) &&
+ sm->sae_pw_id_change)
+ wpa_sm_sae_pw_id_change(sm, ie.sae_pw_ids, ie.sae_pw_ids_len);
sm->msg_3_of_4_ok = 1;
return;
case WPA_PARAM_RSN_OVERRIDE_SUPPORT:
sm->rsn_override_support = value;
break;
+ case WPA_PARAM_SAE_PW_ID_CHANGE:
+ sm->sae_pw_id_change = !!value;
+ break;
default:
break;
}
void (*notify_pmksa_cache_entry)(void *ctx,
struct rsn_pmksa_cache_entry *entry);
void (*ssid_verified)(void *ctx);
+ void (*sae_pw_id_change)(void *ctx, struct wpabuf_array *wa);
};
WPA_PARAM_SPP_AMSDU,
WPA_PARAM_URNM_MFPR,
WPA_PARAM_URNM_MFPR_X20,
+ WPA_PARAM_SAE_PW_ID_CHANGE,
};
enum wpa_rsn_override {
unsigned int prot_range_neg_x20:1;
unsigned int ssid_protection:1;
unsigned int spp_amsdu:1;
+ unsigned int sae_pw_id_change:1;
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
int wpa_gen_rsnxe(struct wpa_sm *sm, u8 *rsnxe, size_t rsnxe_len)
{
u8 *pos = rsnxe;
- u32 capab = 0, tmp;
+ u64 capab = 0, tmp;
size_t flen;
if (wpa_key_mgmt_sae(sm->key_mgmt) &&
capab |= BIT(WLAN_RSNX_CAPAB_SSID_PROTECTION);
if (sm->spp_amsdu)
capab |= BIT(WLAN_RSNX_CAPAB_SPP_A_MSDU);
+ if (sm->sae_pw_id_change)
+ capab |= BIT_ULL(WLAN_RSNX_CAPAB_SAE_PW_ID_CHANGE);
if (!capab)
return 0; /* no supported extended RSN capabilities */
#define BIT(x) (1U << (x))
#endif
+#ifndef BIT_ULL
+#define BIT_ULL(x) (1ULL << (x))
+#endif
+
#ifndef MIN
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif
OBJS += src/common/sae.c
ifdef CONFIG_SAE_PK
L_CFLAGS += -DCONFIG_SAE_PK
-NEED_AES_SIV=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
OBJS += ../src/common/sae_pk.o
endif
+NEED_AES_SIV=y
NEED_ECC=y
NEED_DH_GROUPS=y
NEED_HMAC_SHA256_KDF=y
#endif /* NO_CONFIG_WRITE */
+static int wpa_config_parse_alt_sae_password_ids(const struct parse_data *data,
+ struct wpa_ssid *ssid,
+ int line, const char *value)
+{
+ struct wpabuf *tmp;
+
+ if (!value[0]) {
+ wpabuf_array_free(ssid->alt_sae_password_ids);
+ return 0;
+ }
+
+ tmp = wpabuf_parse_bin(value);
+ if (!tmp) {
+ wpa_printf(MSG_ERROR, "Line %d: invalid alt_sae_password_ids",
+ line);
+ return -1;
+ }
+
+ if (!ssid->alt_sae_password_ids)
+ ssid->alt_sae_password_ids = wpabuf_array_alloc();
+ if (wpabuf_array_add(ssid->alt_sae_password_ids, tmp) < 0)
+ return -1;
+ return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char *
+wpa_config_write_alt_sae_password_ids(const struct parse_data *data,
+ struct wpa_ssid *ssid)
+{
+ return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
static int wpa_config_parse_proto(const struct parse_data *data,
struct wpa_ssid *ssid, int line,
const char *value)
{ INT(mem_only_psk) },
{ STR_KEY(sae_password) },
{ STR(sae_password_id) },
+ { FUNC(alt_sae_password_ids) },
{ INT(sae_pwe) },
{ FUNC(proto) },
{ FUNC(key_mgmt) },
{ INT_RANGE(max_idle, 0, 65535)},
{ INT_RANGE(ssid_protection, 0, 1)},
{ INT_RANGE(rsn_overriding, 0, 2)},
+ { INT_RANGE(sae_password_id_change, 0, 1)},
};
#undef OFFSET
os_free(ssid->ext_psk);
str_clear_free(ssid->sae_password);
os_free(ssid->sae_password_id);
+ wpabuf_array_free(ssid->alt_sae_password_ids);
#ifdef IEEE8021X_EAPOL
eap_peer_config_free(&ssid->eap);
#endif /* IEEE8021X_EAPOL */
STR(sae_password);
STR(sae_password_id);
write_int(f, "sae_pwe", ssid->sae_pwe, DEFAULT_SAE_PWE);
+ INT(sae_password_id_change);
write_proto(f, ssid);
write_key_mgmt(f, ssid);
INT_DEF(bg_scan_period, DEFAULT_BG_SCAN_PERIOD);
INT(max_idle);
INT(ssid_protection);
INT_DEF(rsn_overriding, RSN_OVERRIDING_NOT_SET);
-
+#ifdef CONFIG_SAE
+ if (ssid->alt_sae_password_ids) {
+ struct wpabuf_array *ids = ssid->alt_sae_password_ids;
+ unsigned int idx;
+ char hex[255 * 2 + 1];
+
+ for (idx = 0; idx < ids->num; idx++) {
+ wpa_snprintf_hex(hex, sizeof(hex),
+ wpabuf_head(ids->buf[idx]),
+ wpabuf_len(ids->buf[idx]));
+ fprintf(f, "\talt_sae_password_ids=%s\n", hex);
+ }
+ }
+#endif /* CONFIG_SAE */
#undef STR
#undef INT
#undef INT_DEF
*/
char *sae_password_id;
+ struct wpabuf_array *alt_sae_password_ids;
+ unsigned int alt_sae_passwords_ids_idx;
+ bool alt_sae_passwords_ids_used;
+
struct sae_pt *pt;
/**
*/
int go_dik_id;
+ /**
+ * sae_password_id_change - Whether to use changing SAE password IDs
+ */
+ bool sae_password_id_change;
};
#endif /* CONFIG_SSID_H */
else
wpabuf_put_le16(buf,WLAN_STATUS_SUCCESS);
}
+
+ if (use_pt && ssid->pt && ssid->pt->password_id) {
+ password_id = wpabuf_head(ssid->pt->password_id);
+ password_id_len = wpabuf_len(ssid->pt->password_id);
+ }
if (sae_write_commit(&wpa_s->sme.sae, buf, wpa_s->sme.sae_token,
password_id, password_id_len) < 0) {
wpabuf_free(buf);
if (auth_transaction == 1 &&
status_code == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
const u8 *bssid = sa ? sa : wpa_s->pending_bssid;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ if (ssid && ssid->alt_sae_password_ids &&
+ ssid->alt_sae_passwords_ids_used) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: Remove alternative password identifier (idx=%u) due to rejection",
+ ssid->alt_sae_passwords_ids_idx);
+ wpabuf_array_remove(ssid->alt_sae_password_ids,
+ ssid->alt_sae_passwords_ids_idx);
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf))
+ wpa_printf(MSG_DEBUG,
+ "SAE: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+ }
wpa_msg(wpa_s, MSG_INFO,
WPA_EVENT_SAE_UNKNOWN_PASSWORD_IDENTIFIER MACSTR,
WPA_CIPHER_GCMP) &&
(wpa_s->wpa_proto & WPA_PROTO_RSN));
+ wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_SAE_PW_ID_CHANGE,
+ ssid->sae_password_id && ssid->sae_password_id_change);
+
if (!skip_default_rsne) {
if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie,
wpa_ie_len)) {
const u8 *password_id = (const u8 *) ssid->sae_password_id;
size_t password_id_len = ssid->sae_password_id ?
os_strlen(ssid->sae_password_id) : 0;
+ struct wpabuf_array *ids;
if (!groups || groups[0] <= 0)
groups = default_groups;
return;
}
- if (ssid->pt)
- return; /* PT already derived */
+ ids = ssid->alt_sae_password_ids;
+ if (ids && ids->num) {
+ unsigned int idx = os_random() % ids->num;
+ struct wpabuf *id = ids->buf[idx];
+
+ password_id = wpabuf_head(id);
+ password_id_len = wpabuf_len(id);
+ wpa_hexdump(MSG_DEBUG,
+ "SAE: Prepare PT for alternative password ID",
+ password_id, password_id_len);
+ ssid->alt_sae_passwords_ids_idx = idx;
+ ssid->alt_sae_passwords_ids_used = true;
+ }
+
+ if (ssid->pt) {
+ if (!password_id && !ssid->pt->password_id)
+ return; /* PT already derived for no PW ID */
+ if (password_id && ssid->pt->password_id &&
+ password_id_len == wpabuf_len(ssid->pt->password_id) &&
+ os_memcmp(password_id, wpabuf_head(ssid->pt->password_id),
+ password_id_len == 0))
+ return; /* PT already derived for same PW ID */
+
+ /* PT was derived for another password identifier */
+ sae_deinit_pt(ssid->pt);
+ ssid->pt = NULL;
+ }
ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
(const u8 *) password, os_strlen(password),
password_id, password_id_len);
wpa_msg(wpa_s, MSG_INFO, "RSN: SSID matched expected value");
}
+
+static void wpa_supplicant_sae_pw_id_change(void *_wpa_s,
+ struct wpabuf_array *wa)
+{
+ struct wpa_supplicant *wpa_s = _wpa_s;
+ struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+ wpa_msg(wpa_s, MSG_INFO, "RSN: Received %u SAE Password Identifier(s)",
+ wa->num);
+ if (!ssid) {
+ wpabuf_array_free(wa);
+ return;
+ }
+
+ wpabuf_array_free(ssid->alt_sae_password_ids);
+ ssid->alt_sae_password_ids = wa;
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+ if (wpa_s->conf->update_config &&
+ wpa_config_write(wpa_s->confname, wpa_s->conf))
+ wpa_printf(MSG_DEBUG, "SAE: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
#endif /* CONFIG_NO_WPA */
#endif /* CONFIG_PASN */
ctx->notify_pmksa_cache_entry = wpa_supplicant_notify_pmksa_cache_entry;
ctx->ssid_verified = wpa_supplicant_ssid_verified;
+ ctx->sae_pw_id_change = wpa_supplicant_sae_pw_id_change;
wpa_s->wpa = wpa_sm_init(ctx);
if (wpa_s->wpa == NULL) {