iwd.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 00/18] Basic WPA3 support in AP mode
@ 2024-05-06  0:30 John Brandt
  2024-05-06  0:30 ` [PATCH v2 01/18] ap: ability to advertise PSK and SAE John Brandt
                   ` (18 more replies)
  0 siblings, 19 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

This set of patches adds basic WPA3 support for IWD in AP mode. It has
been tested by connecting to IWD AP using wpa_supplicant, both when WPA3
is enabled and when it was not. A unit test for SAE mode is now also
included and all other unit tests now pass again.

Compared to the previous version, this patch now also includes MFP
support for AP mode. The AP will generate an IGTK on startup, and
distribute it to MFP-capable clients. Sanity checks on received SAE
frames are now also added.

John Brandt (18):
  ap: ability to advertise PSK and SAE
  ap: accept PSK/SAE in auth depending on config
  unit: fix SAE unit tests
  sae: add function sae_set_group
  sae: refactor and add function sae_calculate_keys
  sae: make sae_process_commit callable in AP mode
  sae: verify offered group in AP mode
  sae: support reception of Confirm frame by AP
  ap: add support to handle SAE authentication
  ap: enable start of 4-way HS after SAE
  eapol: support PTK derivation with SHA256
  eapol: encrypt key data for AKM-defined ciphers
  unit: add unit test for SAE AP mode
  ap: move toward requiring MFP when using SAE
  handshake: add functions to save and set IGTK
  eapol: include IGTK in 4-way handshake as AP
  ap: generate IGTK on startup if MFP is enabled
  ap: propogate IGTK and RSC to handshake

 src/ap.c          | 270 ++++++++++++++++++++++++++++++++++++++++------
 src/eapol.c       |  70 +++++++++---
 src/handshake.c   |  34 ++++++
 src/handshake.h   |   8 ++
 src/nl80211util.c |   7 +-
 src/sae.c         | 209 ++++++++++++++++++++++++-----------
 src/wiphy.c       |   2 +-
 src/wiphy.h       |   2 +
 unit/test-sae.c   | 114 +++++++++++++++++++-
 9 files changed, 595 insertions(+), 121 deletions(-)

-- 
2.45.0


^ permalink raw reply	[flat|nested] 32+ messages in thread

* [PATCH v2 01/18] ap: ability to advertise PSK and SAE
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-06  0:30 ` [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config John Brandt
                   ` (17 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Add the configuration option AKMSuites under Security so it becomes
possible to support both PSK and SAE. This influences the advertised
AKMs in the beacon.
---
 src/ap.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/src/ap.c b/src/ap.c
index b4e7593e..d50f9e4f 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -80,6 +80,7 @@ struct ap_state {
 
 	unsigned int ciphers;
 	enum ie_rsn_cipher_suite group_cipher;
+	unsigned int akm_suites;
 	uint32_t beacon_interval;
 	struct l_uintset *rates;
 	uint32_t start_stop_cmd_id;
@@ -631,7 +632,7 @@ static void ap_drop_rsna(struct sta_state *sta)
 static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
 {
 	memset(rsn, 0, sizeof(*rsn));
-	rsn->akm_suites = IE_RSN_AKM_SUITE_PSK;
+	rsn->akm_suites = ap->akm_suites;
 	rsn->pairwise_ciphers = ap->ciphers;
 	rsn->group_cipher = ap->group_cipher;
 }
@@ -3620,6 +3621,7 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 	size_t len;
 	L_AUTO_FREE_VAR(char *, strval) = NULL;
 	_auto_(l_strv_free) char **ciphers_str = NULL;
+	_auto_(l_strv_free) char **akms_str = NULL;
 	uint16_t cipher_mask;
 	int err;
 	int i;
@@ -3838,6 +3840,28 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 		ap->ciphers |= cipher;
 	}
 
+	akms_str = l_settings_get_string_list(config, "Security",
+						"AKMSuites", ',');
+	for (i = 0; akms_str && akms_str[i]; i++) {
+		if (!strcmp(akms_str[i], "PSK"))
+			ap->akm_suites |= IE_RSN_AKM_SUITE_PSK;
+		else if (!strcmp(akms_str[i], "SAE"))
+			ap->akm_suites |= IE_RSN_AKM_SUITE_SAE_SHA256;
+		else {
+			l_warn("Unsupported or unknown AKM suite %s",
+					akms_str[i]);
+			return -ENOTSUP;
+		}
+	}
+
+	if (ap->akm_suites == 0) {
+		/*
+		 * Default behavior if no AKMs are specified but a passphrase
+		 * is to only enable PSK == WPA2.
+		 */
+		 ap->akm_suites |= IE_RSN_AKM_SUITE_PSK;
+	}
+
 	if (!ap->ciphers) {
 		/*
 		 * Default behavior if no ciphers are specified, disable TKIP
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
  2024-05-06  0:30 ` [PATCH v2 01/18] ap: ability to advertise PSK and SAE John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:07   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 03/18] unit: fix SAE unit tests John Brandt
                   ` (16 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

On reception of an authentication frame, accept both PSK and SAE as AKM
depending on the config. Save the client's AKM for later use.
---
 src/ap.c | 29 ++++++++++++++++++++++-------
 1 file changed, 22 insertions(+), 7 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index d50f9e4f..cd253ce3 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -132,6 +132,7 @@ struct sta_state {
 	uint8_t *assoc_ies;
 	size_t assoc_ies_len;
 	uint8_t *assoc_rsne;
+	enum ie_rsn_akm_suite akm_suite;
 	struct eapol_sm *sm;
 	struct handshake_state *hs;
 	uint32_t gtk_query_cmd_id;
@@ -2606,6 +2607,7 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
 	const uint8_t *from = hdr->address_2;
 	const uint8_t *bssid = netdev_get_address(ap->netdev);
 	struct sta_state *sta;
+	enum ie_rsn_akm_suite akm_suite;
 
 	l_info("AP Authentication from %s", util_address_to_string(from));
 
@@ -2627,17 +2629,28 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
 		}
 	}
 
-	/* Only Open System authentication implemented here */
-	if (L_LE16_TO_CPU(auth->algorithm) !=
-			MMPDU_AUTH_ALGO_OPEN_SYSTEM) {
+	if ((ap->akm_suites & IE_RSN_AKM_SUITE_SAE_SHA256) &&
+	    (L_LE16_TO_CPU(auth->algorithm) == MMPDU_AUTH_ALGO_SAE) ) {
+		/* When using SAE it must be COMMIT or CONFIRM frame */
+		if (L_LE16_TO_CPU(auth->transaction_sequence) != 1 &&
+		    L_LE16_TO_CPU(auth->transaction_sequence) != 2) {
+			ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
+			return;
+		}
+		akm_suite = IE_RSN_AKM_SUITE_SAE_SHA256;
+	} else if ((ap->akm_suites & IE_RSN_AKM_SUITE_PSK) &&
+		   (L_LE16_TO_CPU(auth->algorithm) == MMPDU_AUTH_ALGO_OPEN_SYSTEM) ) {
+		/* When using PSK it must be Open System authentication */
+		if (L_LE16_TO_CPU(auth->transaction_sequence) != 1) {
+			ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
+			return;
+		}
+		akm_suite = IE_RSN_AKM_SUITE_PSK;
+	} else {
 		ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
 		return;
 	}
 
-	if (L_LE16_TO_CPU(auth->transaction_sequence) != 1) {
-		ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
-		return;
-	}
 
 	sta = l_queue_find(ap->sta_states, ap_sta_match_addr, from);
 
@@ -2666,6 +2679,8 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
 	if (!ap->sta_states)
 		ap->sta_states = l_queue_new();
 
+	sta->akm_suite = akm_suite;
+
 	l_queue_push_tail(ap->sta_states, sta);
 
 	/*
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 03/18] unit: fix SAE unit tests
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
  2024-05-06  0:30 ` [PATCH v2 01/18] ap: ability to advertise PSK and SAE John Brandt
  2024-05-06  0:30 ` [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:51   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 04/18] sae: add function sae_set_group John Brandt
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Don't mark either client as being the authenticator. In the current unit
tests, both instances act as clients to test functionality. This ensures
the unit does not show an error during the following commits where SAE
for AP mode is added.
---
 unit/test-sae.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/unit/test-sae.c b/unit/test-sae.c
index d9ec6b31..04783d18 100644
--- a/unit/test-sae.c
+++ b/unit/test-sae.c
@@ -421,7 +421,6 @@ static void test_bad_confirm(const void *arg)
 	handshake_state_set_supplicant_address(hs2, aa);
 	handshake_state_set_authenticator_address(hs2, spa);
 	handshake_state_set_passphrase(hs2, passphrase);
-	handshake_state_set_authenticator(hs2, true);
 
 	ap1 = sae_sm_new(hs1, end_to_end_tx_func, test_tx_assoc_func, td1);
 	ap2 = sae_sm_new(hs2, end_to_end_tx_func, test_tx_assoc_func, td2);
@@ -496,7 +495,6 @@ static void test_confirm_after_accept(const void *arg)
 	handshake_state_set_supplicant_address(hs2, aa);
 	handshake_state_set_authenticator_address(hs2, spa);
 	handshake_state_set_passphrase(hs2, passphrase);
-	handshake_state_set_authenticator(hs2, true);
 
 	ap1 = sae_sm_new(hs1, end_to_end_tx_func, test_tx_assoc_func, td1);
 	ap2 = sae_sm_new(hs2, end_to_end_tx_func, test_tx_assoc_func, td2);
@@ -581,7 +579,6 @@ static void test_end_to_end(const void *arg)
 	handshake_state_set_supplicant_address(hs2, aa);
 	handshake_state_set_authenticator_address(hs2, spa);
 	handshake_state_set_passphrase(hs2, passphrase);
-	handshake_state_set_authenticator(hs2, true);
 
 	ap1 = sae_sm_new(hs1, end_to_end_tx_func, test_tx_assoc_func, td1);
 	ap2 = sae_sm_new(hs2, end_to_end_tx_func, test_tx_assoc_func, td2);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 04/18] sae: add function sae_set_group
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (2 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 03/18] unit: fix SAE unit tests John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 14:53   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys John Brandt
                   ` (14 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Refactor code by adding function sae_set_group. This will make the next
commits easier where basic SAE support for APs is added.
---
 src/sae.c | 25 ++++++++++++++-----------
 1 file changed, 14 insertions(+), 11 deletions(-)

diff --git a/src/sae.c b/src/sae.c
index bf9fb0ff..4e0b73d8 100644
--- a/src/sae.c
+++ b/src/sae.c
@@ -148,6 +148,18 @@ static void sae_reset_state(struct sae_sm *sm)
 	sm->pwe = NULL;
 }
 
+static int sae_set_group(struct sae_sm *sm, int group)
+{
+	sm->curve = l_ecc_curve_from_ike_group(group);
+	if (sm->curve == NULL)
+		return -ENOENT;
+
+	sae_debug("Using group %u", group);
+	sm->group = group;
+
+	return 0;
+}
+
 static int sae_choose_next_group(struct sae_sm *sm)
 {
 	const unsigned int *ecc_groups = l_ecc_supported_ike_groups();
@@ -166,9 +178,7 @@ static int sae_choose_next_group(struct sae_sm *sm)
 		sae_debug("Forcing default SAE group 19");
 
 		sm->group_retry++;
-		sm->group = 19;
-
-		goto get_curve;
+		return sae_set_group(sm, 19);
 	}
 
 	do {
@@ -182,14 +192,7 @@ static int sae_choose_next_group(struct sae_sm *sm)
 	if (reset)
 		sae_reset_state(sm);
 
-	sm->group = ecc_groups[sm->group_retry];
-
-get_curve:
-	sae_debug("Using group %u", sm->group);
-
-	sm->curve = l_ecc_curve_from_ike_group(sm->group);
-
-	return 0;
+	return sae_set_group(sm, ecc_groups[sm->group_retry]);
 }
 
 static int sae_valid_group(struct sae_sm *sm, unsigned int group)
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (3 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 04/18] sae: add function sae_set_group John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:13   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 06/18] sae: make sae_process_commit callable in AP mode John Brandt
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Refactor code by moving code to the new function sae_calculate_keys.
This will make it easier in the next commits to add SAE support for AP
mode.
---
 src/sae.c | 86 ++++++++++++++++++++++++++++++++-----------------------
 1 file changed, 50 insertions(+), 36 deletions(-)

diff --git a/src/sae.c b/src/sae.c
index 4e0b73d8..7787a390 100644
--- a/src/sae.c
+++ b/src/sae.c
@@ -685,10 +685,9 @@ static bool sae_send_confirm(struct sae_sm *sm)
 	return true;
 }
 
-static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
-					const uint8_t *frame, size_t len)
+
+static int sae_calculate_keys(struct sae_sm *sm)
 {
-	uint8_t *ptr = (uint8_t *) frame;
 	unsigned int nbytes = l_ecc_curve_get_scalar_bytes(sm->curve);
 	enum l_checksum_type hash =
 		crypto_sae_hash_from_ecc_prime_len(sm->sae_type, nbytes);
@@ -704,39 +703,6 @@ static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
 	struct l_ecc_scalar *tmp_scalar;
 	struct l_ecc_scalar *order;
 
-	ptr += 2;
-
-	sm->p_scalar = l_ecc_scalar_new(sm->curve, ptr, nbytes);
-	if (!sm->p_scalar) {
-		l_error("Server sent invalid P_Scalar during commit");
-		return sae_reject(sm, SAE_STATE_COMMITTED,
-				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
-	}
-
-	ptr += nbytes;
-
-	sm->p_element = l_ecc_point_from_data(sm->curve, L_ECC_POINT_TYPE_FULL,
-						ptr, nbytes * 2);
-	if (!sm->p_element) {
-		l_error("Server sent invalid P_Element during commit");
-		return sae_reject(sm, SAE_STATE_COMMITTED,
-				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
-	}
-
-	/*
-	 * If they match those sent as part of the protocol instance's own
-	 * SAE Commit message, the frame shall be silently discarded (because
-	 * it is evidence of a reflection attack) and the t0 (retransmission)
-	 * timer shall be set.
-	 */
-	if (l_ecc_scalars_are_equal(sm->p_scalar, sm->scalar) ||
-			l_ecc_points_are_equal(sm->p_element, sm->element)) {
-		l_warn("peer scalar or element matched own, discarding frame");
-		return -ENOMSG;
-	}
-
-	sm->sc++;
-
 	/*
 	 * K = scalar-op(rand, (element-op(scalar-op(peer-commit-scalar, PWE),
 	 *			PEER-COMMIT-ELEMENT)))
@@ -825,6 +791,54 @@ static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
 	/* don't set the handshakes pmkid until confirm is verified */
 	memcpy(sm->pmkid, tmp, 16);
 
+	return 0;
+}
+
+
+static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
+					const uint8_t *frame, size_t len)
+{
+	uint8_t *ptr = (uint8_t *) frame;
+	unsigned int nbytes = l_ecc_curve_get_scalar_bytes(sm->curve);
+	int r;
+
+	ptr += 2;
+
+	sm->p_scalar = l_ecc_scalar_new(sm->curve, ptr, nbytes);
+	if (!sm->p_scalar) {
+		l_error("Server sent invalid P_Scalar during commit");
+		return sae_reject(sm, SAE_STATE_COMMITTED,
+				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
+	}
+
+	ptr += nbytes;
+
+	sm->p_element = l_ecc_point_from_data(sm->curve, L_ECC_POINT_TYPE_FULL,
+						ptr, nbytes * 2);
+	if (!sm->p_element) {
+		l_error("Server sent invalid P_Element during commit");
+		return sae_reject(sm, SAE_STATE_COMMITTED,
+				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
+	}
+
+	/*
+	 * If they match those sent as part of the protocol instance's own
+	 * SAE Commit message, the frame shall be silently discarded (because
+	 * it is evidence of a reflection attack) and the t0 (retransmission)
+	 * timer shall be set.
+	 */
+	if (l_ecc_scalars_are_equal(sm->p_scalar, sm->scalar) ||
+			l_ecc_points_are_equal(sm->p_element, sm->element)) {
+		l_warn("peer scalar or element matched own, discarding frame");
+		return -ENOMSG;
+	}
+
+	sm->sc++;
+
+	r = sae_calculate_keys(sm);
+	if (r != 0)
+		return r;
+
 	if (!sae_send_confirm(sm))
 		return -EPROTO;
 
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 06/18] sae: make sae_process_commit callable in AP mode
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (4 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-06  0:30 ` [PATCH v2 07/18] sae: verify offered group " John Brandt
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

As an AP, the function sae_process_commit will pick the group offered by
the client. In a subsuquent commit the offered group will first be
verified before calling sae_process_commit. The AP will reply with a
Commit frame, calculate current keys, and move to the COMMITTED state.
---
 src/sae.c | 67 ++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 20 deletions(-)

diff --git a/src/sae.c b/src/sae.c
index 7787a390..7ba9b0eb 100644
--- a/src/sae.c
+++ b/src/sae.c
@@ -48,6 +48,8 @@ static bool debug;
 #define SAE_SYNC_MAX		3
 #define SAE_MAX_ASSOC_RETRY	3
 
+static bool sae_send_commit(struct sae_sm *sm, bool retry);
+
 #define sae_debug(fmat, ...) \
 ({	\
 	if (debug) \
@@ -321,6 +323,21 @@ static ssize_t sae_cn(struct sae_sm *sm, uint16_t send_confirm,
 	return ret;
 }
 
+static bool sae_check_reflection(struct sae_sm *sm)
+{
+	/*
+	 * If they match those sent as part of the protocol instance's own
+	 * SAE Commit message, the frame shall be silently discarded (because
+	 * it is evidence of a reflection attack) and the t0 (retransmission)
+	 * timer shall be set.
+	 */
+	if ((sm->scalar && sm->p_scalar && l_ecc_scalars_are_equal(sm->scalar, sm->p_scalar)) ||
+	    (sm->element && sm->p_element && l_ecc_points_are_equal(sm->element, sm->p_element)))
+		return false;
+
+	return true;
+}
+
 static int sae_reject(struct sae_sm *sm, uint16_t transaction, uint16_t status)
 {
 	uint8_t reject[6];
@@ -593,6 +610,10 @@ static int sae_build_commit(struct sae_sm *sm, const uint8_t *addr1,
 
 	l_ecc_scalar_free(mask);
 
+	/* ensure the scalar and element are different from peer */
+	if (!sae_check_reflection(sm))
+		return -EPROTO;
+
 	/*
 	 * Several cases require retransmitting the same commit message. The
 	 * anti-clogging code path requires this as well as the retransmission
@@ -799,9 +820,13 @@ static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
 					const uint8_t *frame, size_t len)
 {
 	uint8_t *ptr = (uint8_t *) frame;
-	unsigned int nbytes = l_ecc_curve_get_scalar_bytes(sm->curve);
+	unsigned int nbytes;
 	int r;
 
+	if (sm->handshake->authenticator && sae_set_group(sm, l_get_le16(frame)) < 0)
+		return -1;
+
+	nbytes = l_ecc_curve_get_scalar_bytes(sm->curve);
 	ptr += 2;
 
 	sm->p_scalar = l_ecc_scalar_new(sm->curve, ptr, nbytes);
@@ -821,28 +846,30 @@ static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
 				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
 	}
 
-	/*
-	 * If they match those sent as part of the protocol instance's own
-	 * SAE Commit message, the frame shall be silently discarded (because
-	 * it is evidence of a reflection attack) and the t0 (retransmission)
-	 * timer shall be set.
-	 */
-	if (l_ecc_scalars_are_equal(sm->p_scalar, sm->scalar) ||
-			l_ecc_points_are_equal(sm->p_element, sm->element)) {
-		l_warn("peer scalar or element matched own, discarding frame");
-		return -ENOMSG;
-	}
-
 	sm->sc++;
 
-	r = sae_calculate_keys(sm);
-	if (r != 0)
-		return r;
+	if (sm->handshake->authenticator) {
+		if (!sae_send_commit(sm, false))
+			return -EPROTO;
+
+		r = sae_calculate_keys(sm);
+		if (r != 0)
+			return r;
 
-	if (!sae_send_confirm(sm))
-		return -EPROTO;
+		sm->state = SAE_STATE_COMMITTED;
+	} else {
+		if (!sae_check_reflection(sm))
+			return -EPROTO;
 
-	sm->state = SAE_STATE_CONFIRMED;
+		r = sae_calculate_keys(sm);
+		if (r != 0)
+			return r;
+
+		if (!sae_send_confirm(sm))
+			return -EPROTO;
+
+		sm->state = SAE_STATE_CONFIRMED;
+	}
 
 	return 0;
 }
@@ -1013,7 +1040,7 @@ static int sae_verify_nothing(struct sae_sm *sm, uint16_t transaction,
 	/*
 	 * TODO: This does not handle the transition from NOTHING -> CONFIRMED
 	 * as this is only relevant to the AP or in Mesh mode which is not
-	 * yet supported.
+	 * yet fully supported.
 	 */
 	if (transaction != SAE_STATE_COMMITTED)
 		return -EBADMSG;
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 07/18] sae: verify offered group in AP mode
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (5 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 06/18] sae: make sae_process_commit callable in AP mode John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:11   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 08/18] sae: support reception of Confirm frame by AP John Brandt
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When receiving a Commit frame in AP mode, first verify that we support
the offered group before further processing the frame.
---
 src/sae.c | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/src/sae.c b/src/sae.c
index 7ba9b0eb..7405a561 100644
--- a/src/sae.c
+++ b/src/sae.c
@@ -216,6 +216,18 @@ static int sae_valid_group(struct sae_sm *sm, unsigned int group)
 	return -ENOENT;
 }
 
+static int sae_supported_group(struct sae_sm *sm, unsigned int group)
+{
+	const unsigned int *ecc_groups = l_ecc_supported_ike_groups();
+	unsigned int i;
+
+	for (i = 0; ecc_groups[i]; i++)
+		if (ecc_groups[i] == group)
+			return true;
+
+	return false;
+}
+
 static bool sae_pwd_seed(const uint8_t *addr1, const uint8_t *addr2,
 				uint8_t *base, size_t base_len,
 				uint8_t counter, uint8_t *out)
@@ -1053,7 +1065,8 @@ static int sae_verify_nothing(struct sae_sm *sm, uint16_t transaction,
 		return -EBADMSG;
 
 	/* reject with unsupported group */
-	if (l_get_le16(frame) != sm->group)
+	if ((sm->handshake->authenticator && sae_supported_group(sm, l_get_le16(frame)) < 0) ||
+	    (!sm->handshake->authenticator && l_get_le16(frame) != sm->group))
 		return sae_reject(sm, SAE_STATE_COMMITTED,
 				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
 
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 08/18] sae: support reception of Confirm frame by AP
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (6 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 07/18] sae: verify offered group " John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:51   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 09/18] ap: add support to handle SAE authentication John Brandt
                   ` (10 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Experimental AP-mode support for receiving a Confirm frame when in the
COMMITTED state. The AP will reply with a Confirm frame.

Note that when acting as an AP, on reception of a Commit frame, the AP
only replies with a Commit frame. The protocols allows to also already
send the Confirm frame, but older clients may not support simultaneously
receiving a Commit and Confirm frame.
---
 src/sae.c | 48 +++++++++++++++++++++++++++++++++++-------------
 1 file changed, 35 insertions(+), 13 deletions(-)

diff --git a/src/sae.c b/src/sae.c
index 7405a561..3ad28fab 100644
--- a/src/sae.c
+++ b/src/sae.c
@@ -930,9 +930,13 @@ static int sae_process_confirm(struct sae_sm *sm, const uint8_t *from,
 
 	sm->state = SAE_STATE_ACCEPTED;
 
-	sae_debug("Sending Associate to "MAC, MAC_STR(sm->handshake->aa));
-
-	sm->tx_assoc(sm->user_data);
+	if (!sm->handshake->authenticator) {
+		sae_debug("Sending Associate to "MAC, MAC_STR(sm->handshake->aa));
+		sm->tx_assoc(sm->user_data);
+	} else {
+		if (!sae_send_confirm(sm))
+			return -EPROTO;
+	}
 
 	return 0;
 }
@@ -1083,16 +1087,34 @@ static int sae_verify_committed(struct sae_sm *sm, uint16_t transaction,
 	unsigned int skip;
 	struct ie_tlv_iter iter;
 
-	/*
-	 * Upon receipt of a Con event...
-	 * Then the protocol instance checks the value of Sync. If it
-	 * is greater than dot11RSNASAESync, the protocol instance shall send a
-	 * Del event to the parent process and transition back to Nothing state.
-	 * If Sync is not greater than dot11RSNASAESync, the protocol instance
-	 * shall increment Sync, transmit the last SAE Commit message sent to
-	 * the peer...
-	 */
-	if (transaction == SAE_STATE_CONFIRMED) {
+	if (sm->handshake->authenticator && transaction == SAE_STATE_CONFIRMED) {
+		enum l_checksum_type hash =
+			crypto_sae_hash_from_ecc_prime_len(sm->sae_type,
+				l_ecc_curve_get_scalar_bytes(sm->curve));
+		size_t hash_len = l_checksum_digest_length(hash);
+
+		if (len < hash_len + 2) {
+			l_error("SAE: Confirm packet too short");
+			return -EBADMSG;
+		}
+
+		/*
+		 * TODO: Add extra functionality such as supporting anti-clogging
+		 * tokens and tracking rejected groups. Note that the cryptographic
+		 * confirm field value will be checked at a later point.
+		 */
+
+		return 0;
+	} else if (transaction == SAE_STATE_CONFIRMED) {
+		/*
+		 * Upon receipt of a Con event...
+		 * Then the protocol instance checks the value of Sync. If it
+		 * is greater than dot11RSNASAESync, the protocol instance shall send a
+		 * Del event to the parent process and transition back to Nothing state.
+		 * If Sync is not greater than dot11RSNASAESync, the protocol instance
+		 * shall increment Sync, transmit the last SAE Commit message sent to
+		 * the peer...
+		 */
 		if (sm->sync > SAE_SYNC_MAX)
 			return -ETIMEDOUT;
 
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 09/18] ap: add support to handle SAE authentication
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (7 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 08/18] sae: support reception of Confirm frame by AP John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:44   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 10/18] ap: enable start of 4-way HS after SAE John Brandt
                   ` (9 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When the client requests SAE authentication, and it is enabled, allocate
an auth_proto instance to handle SAE authentication. This also adds a
new function to send SAE frames in AP mode that can be used by the
auth_proto instance.
---
 src/ap.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 58 insertions(+), 9 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index cd253ce3..ec27180e 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -59,6 +59,8 @@
 #include "src/diagnostic.h"
 #include "src/band.h"
 #include "src/common.h"
+#include "src/sae.h"
+#include "src/auth-proto.h"
 
 struct ap_state {
 	struct netdev *netdev;
@@ -132,6 +134,7 @@ struct sta_state {
 	uint8_t *assoc_ies;
 	size_t assoc_ies_len;
 	uint8_t *assoc_rsne;
+	struct auth_proto *auth_proto;
 	enum ie_rsn_akm_suite akm_suite;
 	struct eapol_sm *sm;
 	struct handshake_state *hs;
@@ -2595,6 +2598,38 @@ static void ap_auth_reply(struct ap_state *ap, const uint8_t *dest,
 				ap_auth_reply_cb, NULL);
 }
 
+static void sae_auth_reply(const uint8_t *data, size_t len, void *user_data)
+{
+	struct sta_state *sta = (struct sta_state *)user_data;
+	struct ap_state *ap = (struct ap_state *)sta->ap;
+	const uint8_t *addr = netdev_get_address(ap->netdev);
+	uint8_t sae_buf[512];
+	struct mmpdu_header *mpdu = (struct mmpdu_header *) sae_buf;
+	struct mmpdu_authentication *auth;
+
+	if (L_WARN_ON(26 + len > sizeof(sae_buf)))
+		return;
+
+	memset(mpdu, 0, sizeof(*mpdu));
+
+	/* Header */
+	mpdu->fc.protocol_version = 0;
+	mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
+	mpdu->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION;
+	memcpy(mpdu->address_1, sta->addr, 6);	/* DA */
+	memcpy(mpdu->address_2, addr, 6);	/* SA */
+	memcpy(mpdu->address_3, addr, 6);	/* BSSID */
+
+	/* Authentication body */
+	auth = (void *) mmpdu_body(mpdu);
+	auth->algorithm = L_CPU_TO_LE16(MMPDU_AUTH_ALGO_SAE);
+
+	/* SAE elements */
+	memcpy(sae_buf + 26, data, len);
+	ap_send_mgmt_frame(ap, mpdu, 26 + len,
+				ap_auth_reply_cb, NULL);
+}
+
 /*
  * 802.11-2016 9.3.3.12 (frame format), 802.11-2016 11.3.4.3 and
  * 802.11-2016 12.3.3.2 (MLME/SME)
@@ -2665,7 +2700,7 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
 	 * than State 1."
 	 */
 	if (sta)
-		goto done;
+		goto have_sta;
 
 	/*
 	 * Per 12.3.3.2.3 with Open System the state change is immediate,
@@ -2680,18 +2715,32 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
 		ap->sta_states = l_queue_new();
 
 	sta->akm_suite = akm_suite;
+	if (sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
+		sta->hs = netdev_handshake_state_new(sta->ap->netdev);
+		handshake_state_set_authenticator(sta->hs, true);
+		handshake_state_set_passphrase(sta->hs, ap->passphrase);
+		handshake_state_set_supplicant_address(sta->hs, sta->addr);
+		handshake_state_set_authenticator_address(sta->hs, bssid);
+
+		sta->auth_proto = sae_sm_new(sta->hs, sae_auth_reply, NULL, sta);
+	}
 
 	l_queue_push_tail(ap->sta_states, sta);
 
-	/*
-	 * Nothing to do here netlink-wise as we can't receive any data
-	 * frames until after association anyway.  We do need to add a
-	 * timeout for the authentication and possibly the kernel could
-	 * handle that if we registered the STA with NEW_STATION now (TODO)
-	 */
 
-done:
-	ap_auth_reply(ap, from, 0);
+have_sta:
+	if (sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
+		auth_proto_rx_authenticate(sta->auth_proto, (const uint8_t *)hdr,
+					   body + body_len - (void *) hdr);
+	} else {
+		/*
+		 * Nothing to do here netlink-wise as we can't receive any data
+		 * frames until after association anyway.  We do need to add a
+		 * timeout for the authentication and possibly the kernel could
+		 * handle that if we registered the STA with NEW_STATION now (TODO)
+		 */
+		ap_auth_reply(ap, from, 0);
+	}
 }
 
 /* 802.11-2016 9.3.3.13 (frame format), 802.11-2016 11.3.4.5 (MLME/SME) */
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 10/18] ap: enable start of 4-way HS after SAE
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (8 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 09/18] ap: add support to handle SAE authentication John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-06  0:30 ` [PATCH v2 11/18] eapol: support PTK derivation with SHA256 John Brandt
                   ` (8 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Accept association frames that request SAE if SAE is enabled by the AP.
When SAE is being used, get the PMK as negoticated by SAE.
---
 src/ap.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index ec27180e..ae406e16 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -1500,12 +1500,19 @@ static void ap_handshake_event(struct handshake_state *hs,
 
 static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
 {
-	/* this handshake setup assumes PSK network */
-	sta->hs = netdev_handshake_state_new(sta->ap->netdev);
-	handshake_state_set_authenticator(sta->hs, true);
+	/* this handshake setup assumes SAE or PSK network */
+	if (sta->hs && sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
+		handshake_state_set_pmk(sta->hs, sta->hs->pmk, 32);
+		handshake_state_set_pmkid(sta->hs, sta->hs->pmkid);
+	} else {
+		sta->hs = netdev_handshake_state_new(sta->ap->netdev);
+		handshake_state_set_authenticator(sta->hs, true);
+		handshake_state_set_pmk(sta->hs, sta->ap->psk, 32);
+	}
+
 	handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
 	handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
-	handshake_state_set_pmk(sta->hs, sta->ap->psk, 32);
+
 	ap_start_handshake(sta, false, gtk_rsc);
 }
 
@@ -2258,7 +2265,7 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 			goto unsupported;
 		}
 
-		if (rsn_info.akm_suites != IE_RSN_AKM_SUITE_PSK) {
+		if ((rsn_info.akm_suites & ap->akm_suites) == 0) {
 			err = MMPDU_REASON_CODE_INVALID_AKMP;
 			goto unsupported;
 		}
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 11/18] eapol: support PTK derivation with SHA256
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (9 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 10/18] ap: enable start of 4-way HS after SAE John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 15:52   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers John Brandt
                   ` (7 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Support PTK derivation in case the negotiated AKM requires SHA256. This
is needed to support SAE in AP mode.
---
 src/eapol.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/eapol.c b/src/eapol.c
index 3ce14d5c..a9b4f3ba 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -1560,6 +1560,7 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
 	size_t ptk_size;
 	const uint8_t *kck;
 	const uint8_t *aa = sm->handshake->aa;
+	enum l_checksum_type type;
 
 	l_debug("ifindex=%u", sm->handshake->ifindex);
 
@@ -1571,12 +1572,16 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
 
 	ptk_size = handshake_state_get_ptk_size(sm->handshake);
 
+	type = L_CHECKSUM_SHA1;
+	if (sm->handshake->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256)
+		type = L_CHECKSUM_SHA256;
+
 	if (!crypto_derive_pairwise_ptk(sm->handshake->pmk,
 					sm->handshake->pmk_len,
 					sm->handshake->spa, aa,
 					sm->handshake->anonce, ek->key_nonce,
 					sm->handshake->ptk, ptk_size,
-					L_CHECKSUM_SHA1))
+					type))
 		return;
 
 	kck = handshake_state_get_kck(sm->handshake);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (10 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 11/18] eapol: support PTK derivation with SHA256 John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 16:04   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 13/18] unit: add unit test for SAE AP mode John Brandt
                   ` (6 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Support encrypting key data when the cipher is AKM-defined. This is
needed to support SAE in AP mode.
---
 src/eapol.c | 51 ++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 36 insertions(+), 15 deletions(-)

diff --git a/src/eapol.c b/src/eapol.c
index a9b4f3ba..524a26c9 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -387,6 +387,23 @@ error:
 	return NULL;
 }
 
+static int padded_aes_wrap(const uint8_t *kek, uint8_t *key_data,
+				size_t *key_data_len,
+				struct eapol_key *out_frame, size_t mic_len)
+{
+	if (*key_data_len < 16 || *key_data_len % 8)
+		key_data[(*key_data_len)++] = 0xdd;
+	while (*key_data_len < 16 || *key_data_len % 8)
+		key_data[(*key_data_len)++] = 0x00;
+
+	if (!aes_wrap(kek, key_data, *key_data_len,
+				EAPOL_KEY_DATA(out_frame, mic_len)))
+		return -ENOPROTOOPT;
+
+	*key_data_len += 8;
+	return 0;
+}
+
 /*
  * Pad and encrypt the plaintext Key Data contents in @key_data using
  * the encryption scheme required by @out_frame->key_descriptor_version,
@@ -395,12 +412,12 @@ error:
  * Note that for efficiency @key_data is being modified, including in
  * case of failure, so it must be sufficiently larger than @key_data_len.
  */
-static int eapol_encrypt_key_data(const uint8_t *kek, uint8_t *key_data,
-				size_t key_data_len,
+static int eapol_encrypt_key_data(enum ie_rsn_akm_suite akm, const uint8_t *kek,
+				uint8_t *key_data, size_t key_data_len,
 				struct eapol_key *out_frame, size_t mic_len)
 {
 	uint8_t key[32];
-	bool ret;
+	int ret;
 
 	switch (out_frame->key_descriptor_version) {
 	case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_MD5_ARC4:
@@ -426,18 +443,21 @@ static int eapol_encrypt_key_data(const uint8_t *kek, uint8_t *key_data,
 		break;
 	case EAPOL_KEY_DESCRIPTOR_VERSION_HMAC_SHA1_AES:
 	case EAPOL_KEY_DESCRIPTOR_VERSION_AES_128_CMAC_AES:
-		if (key_data_len < 16 || key_data_len % 8)
-			key_data[key_data_len++] = 0xdd;
-		while (key_data_len < 16 || key_data_len % 8)
-			key_data[key_data_len++] = 0x00;
-
-		if (!aes_wrap(kek, key_data, key_data_len,
-					EAPOL_KEY_DATA(out_frame, mic_len)))
-			return -ENOPROTOOPT;
-
-		key_data_len += 8;
+		ret = padded_aes_wrap(kek, key_data, &key_data_len, out_frame, mic_len);
+		if (ret < 0)
+			return ret;
 
 		break;
+	case EAPOL_KEY_DESCRIPTOR_VERSION_AKM_DEFINED:
+		switch (akm) {
+		case IE_RSN_AKM_SUITE_SAE_SHA256:
+			ret = padded_aes_wrap(kek, key_data, &key_data_len, out_frame, mic_len);
+			if (ret < 0)
+				return ret;
+			break;
+		default:
+			return -ENOTSUP;
+		}
 	}
 
 	l_put_be16(key_data_len, EAPOL_KEY_DATA(out_frame, mic_len) - 2);
@@ -1467,8 +1487,9 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
 	}
 
 	kek = handshake_state_get_kek(sm->handshake);
-	key_data_len = eapol_encrypt_key_data(kek, key_data_buf,
-						key_data_len, ek, sm->mic_len);
+	key_data_len = eapol_encrypt_key_data(sm->handshake->akm_suite, kek,
+						key_data_buf, key_data_len, ek,
+						sm->mic_len);
 	explicit_bzero(key_data_buf, sizeof(key_data_buf));
 
 	if (key_data_len < 0)
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 13/18] unit: add unit test for SAE AP mode
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (11 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-06  0:30 ` [PATCH v2 14/18] ap: move toward requiring MFP when using SAE John Brandt
                   ` (5 subsequent siblings)
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

Add unit test that simulates client and AP in SAE handshake. Test that
both the client and AP complete the SAE handshake.
---
 unit/test-sae.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 111 insertions(+)

diff --git a/unit/test-sae.c b/unit/test-sae.c
index 04783d18..87e3554d 100644
--- a/unit/test-sae.c
+++ b/unit/test-sae.c
@@ -631,6 +631,116 @@ static void test_end_to_end(const void *arg)
 	l_free(td2);
 }
 
+static void ap_to_client_tx_func(const uint8_t *frame, size_t len, void *user_data)
+{
+	struct test_data *td = user_data;
+	uint16_t trans;
+
+	memcpy(td->tx_packet, frame, len);
+	td->tx_packet_len = len;
+
+	if (len <= 6 && l_get_le16(frame + 2) != 0) {
+		td->tx_reject_occurred = true;
+		return;
+	}
+
+	trans = l_get_le16(frame);	/* transaction */
+
+	switch (trans) {
+	case 1:
+		assert(l_get_le16(frame + 2) == 0);	/* status */
+		assert(l_get_le16(frame + 4) == 19);	/* group */
+
+		td->commit_success = true;
+
+		return;
+	case 2:
+		assert(l_get_le16(frame + 2) == 0);
+		assert(len == 38);
+
+		td->confirm_success = true;
+
+		return;
+	}
+
+	assert(false);
+}
+
+static void test_client_to_ap(const void *arg)
+{
+	struct auth_proto *ap_sta;
+	struct auth_proto *ap_ap;
+	struct test_data *td_sta = l_new(struct test_data, 1);
+	struct test_data *td_ap = l_new(struct test_data, 1);
+	struct handshake_state *hs_sta = test_handshake_state_new(1);
+	struct handshake_state *hs_ap = test_handshake_state_new(2);
+	struct authenticate_frame *frame = alloca(
+				sizeof(struct authenticate_frame) + 512);
+	struct associate_frame *assoc = alloca(sizeof(struct associate_frame));
+	size_t frame_len;
+
+	td_sta->status = 0xffff;
+	td_ap->status = 0xffff;
+
+	handshake_state_set_supplicant_address(hs_sta, spa);
+	handshake_state_set_authenticator_address(hs_sta, aa);
+	handshake_state_set_passphrase(hs_sta, passphrase);
+
+	handshake_state_set_supplicant_address(hs_ap, aa);
+	handshake_state_set_authenticator_address(hs_ap, spa);
+	handshake_state_set_passphrase(hs_ap, passphrase);
+	handshake_state_set_authenticator(hs_ap, true);
+
+	ap_sta = sae_sm_new(hs_sta, end_to_end_tx_func, test_tx_assoc_func, td_sta);
+	ap_ap = sae_sm_new(hs_ap, ap_to_client_tx_func, test_tx_assoc_func, td_ap);
+
+	/* let client send a commit */
+	auth_proto_start(ap_sta);
+
+	/* forward commit from client to the AP */
+	frame_len = setup_auth_frame(frame, aa, 1, 0, td_sta->tx_packet + 4,
+					td_sta->tx_packet_len - 4);
+	assert(auth_proto_rx_authenticate(ap_ap, (uint8_t *)frame,
+						frame_len) == 0);
+
+	/* forward commit from AP to the client */
+	frame_len = setup_auth_frame(frame, spa, 1, 0, td_ap->tx_packet + 4,
+					td_ap->tx_packet_len - 4);
+	assert(auth_proto_rx_authenticate(ap_sta, (uint8_t *)frame,
+						frame_len) == 0);
+
+	/* forward confirm from client to the AP */
+	frame_len = setup_auth_frame(frame, aa, 2, 0, td_sta->tx_packet + 4,
+					td_sta->tx_packet_len - 4);
+	assert(auth_proto_rx_authenticate(ap_ap, (uint8_t *)frame,
+						frame_len) == 0);
+
+	/* forward confirm from AP to the client */
+	frame_len = setup_auth_frame(frame, spa, 2, 0, td_ap->tx_packet + 4,
+					td_ap->tx_packet_len - 4);
+	assert(auth_proto_rx_authenticate(ap_sta, (uint8_t *)frame,
+						frame_len) == 0);
+
+	/* client should by now have sent association request to AP */
+	assert(td_sta->tx_assoc_called);
+	/* AP should have sent a confirm frame to complete handshake */
+	assert(td_ap->confirm_success);
+
+	/* confirm association frame doesn't return an error */
+	frame_len = setup_assoc_frame(assoc, 0);
+	assert(auth_proto_rx_associate(ap_sta, (uint8_t *)assoc, frame_len) == 0);
+	assert(auth_proto_rx_associate(ap_ap, (uint8_t *)assoc, frame_len) == 0);
+
+	handshake_state_free(hs_sta);
+	handshake_state_free(hs_ap);
+
+	auth_proto_free(ap_sta);
+	auth_proto_free(ap_ap);
+
+	l_free(td_sta);
+	l_free(td_ap);
+}
+
 static void test_pt_pwe(const void *data)
 {
 	static const char *ssid = "byteme";
@@ -893,6 +1003,7 @@ int main(int argc, char *argv[])
 	l_test_add("SAE bad confirm", test_bad_confirm, NULL);
 	l_test_add("SAE confirm after accept", test_confirm_after_accept, NULL);
 	l_test_add("SAE end-to-end", test_end_to_end, NULL);
+	l_test_add("SAE client-to-ap", test_client_to_ap, NULL);
 
 	l_test_add("SAE pt-pwe", test_pt_pwe, NULL);
 
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 14/18] ap: move toward requiring MFP when using SAE
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (12 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 13/18] unit: add unit test for SAE AP mode John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 16:12   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 15/18] handshake: add functions to save and set IGTK John Brandt
                   ` (4 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When wanting to use SAE, confirm that MFP is also supported, and
automatically enable MFP. Advertise as MFP capable in the beacon.
---
 src/ap.c    | 13 +++++++++++--
 src/wiphy.c |  2 +-
 src/wiphy.h |  2 ++
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index ae406e16..8cebef42 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -82,6 +82,7 @@ struct ap_state {
 
 	unsigned int ciphers;
 	enum ie_rsn_cipher_suite group_cipher;
+	enum ie_rsn_cipher_suite group_management_cipher;
 	unsigned int akm_suites;
 	uint32_t beacon_interval;
 	struct l_uintset *rates;
@@ -93,6 +94,7 @@ struct ap_state {
 	struct l_timeout *wsc_pbc_timeout;
 	uint16_t wsc_dpid;
 	uint8_t wsc_uuid_r[16];
+	bool mfpc;
 
 	uint16_t last_aid;
 	struct l_queue *sta_states;
@@ -639,6 +641,9 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
 	rsn->akm_suites = ap->akm_suites;
 	rsn->pairwise_ciphers = ap->ciphers;
 	rsn->group_cipher = ap->group_cipher;
+
+	rsn->group_management_cipher = ap->group_management_cipher;
+	rsn->mfpc = ap->mfpc;
 }
 
 static void ap_wsc_exit_pbc(struct ap_state *ap)
@@ -3916,9 +3921,13 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 	for (i = 0; akms_str && akms_str[i]; i++) {
 		if (!strcmp(akms_str[i], "PSK"))
 			ap->akm_suites |= IE_RSN_AKM_SUITE_PSK;
-		else if (!strcmp(akms_str[i], "SAE"))
+		else if (!strcmp(akms_str[i], "SAE")) {
+			if (!wiphy_can_connect_sae(wiphy))
+				return -ENOTSUP;
 			ap->akm_suites |= IE_RSN_AKM_SUITE_SAE_SHA256;
-		else {
+			ap->group_management_cipher = IE_RSN_CIPHER_SUITE_BIP_CMAC;
+			ap->mfpc = true;
+		} else {
 			l_warn("Unsupported or unknown AKM suite %s",
 					akms_str[i]);
 			return -ENOTSUP;
diff --git a/src/wiphy.c b/src/wiphy.c
index fb36ebb2..fb30e7a6 100644
--- a/src/wiphy.c
+++ b/src/wiphy.c
@@ -195,7 +195,7 @@ uint16_t wiphy_get_supported_ciphers(struct wiphy *wiphy, uint16_t mask)
 	return wiphy->supported_ciphers & mask;
 }
 
-static bool wiphy_can_connect_sae(struct wiphy *wiphy)
+bool wiphy_can_connect_sae(struct wiphy *wiphy)
 {
 	/*
 	 * WPA3 Specification version 3, Section 2.2:
diff --git a/src/wiphy.h b/src/wiphy.h
index bc82a007..9472b253 100644
--- a/src/wiphy.h
+++ b/src/wiphy.h
@@ -72,6 +72,8 @@ enum ie_rsn_cipher_suite wiphy_select_cipher(struct wiphy *wiphy,
 							uint16_t mask);
 uint16_t wiphy_get_supported_ciphers(struct wiphy *wiphy, uint16_t mask);
 
+bool wiphy_can_connect_sae(struct wiphy *wiphy);
+
 enum ie_rsn_akm_suite wiphy_select_akm(struct wiphy *wiphy,
 					const struct scan_bss *bss,
 					enum security security,
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 15/18] handshake: add functions to save and set IGTK
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (13 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 14/18] ap: move toward requiring MFP when using SAE John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 16:20   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP John Brandt
                   ` (3 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

To add MFP support in the AP mode, add utility functions to save the
IGTK and to add the IGTK to handshake messages.
---
 src/handshake.c | 34 ++++++++++++++++++++++++++++++++++
 src/handshake.h |  8 ++++++++
 2 files changed, 42 insertions(+)

diff --git a/src/handshake.c b/src/handshake.c
index ee23dbad..fc1978df 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -838,6 +838,21 @@ void handshake_state_set_gtk(struct handshake_state *s, const uint8_t *key,
 	memcpy(s->gtk_rsc, rsc, 6);
 }
 
+void handshake_state_set_igtk(struct handshake_state *s, const uint8_t *key,
+				unsigned int key_index, const uint8_t *rsc)
+{
+	enum crypto_cipher cipher =
+		ie_rsn_cipher_suite_to_cipher(s->group_management_cipher);
+	int key_len = crypto_cipher_key_len(cipher);
+
+	if (!key_len)
+		return;
+
+	memcpy(s->igtk, key, key_len);
+	s->igtk_index = key_index;
+	memcpy(s->igtk_rsc, rsc, 6);
+}
+
 /*
  * This function performs a match of the RSN/WPA IE obtained from the scan
  * results vs the RSN/WPA IE obtained as part of the 4-way handshake.  If they
@@ -1027,6 +1042,25 @@ void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
 	memcpy(to, key, key_len);
 }
 
+void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
+					unsigned int key_index, uint8_t *to)
+{
+	size_t key_len = crypto_cipher_key_len(cipher);
+
+	*to++ = IE_TYPE_VENDOR_SPECIFIC;
+	*to++ = 12 + key_len;
+	l_put_be32(HANDSHAKE_KDE_IGTK, to);
+	to += 4;
+	*to++ = key_index;
+	*to++ = 0;
+
+	/** Initialize PN to zero **/
+	memset(to, 0, 6);
+	to += 6;
+
+	memcpy(to, key, key_len);
+}
+
 static const uint8_t *handshake_state_get_ft_fils_kek(struct handshake_state *s,
 						size_t *len)
 {
diff --git a/src/handshake.h b/src/handshake.h
index 62118fe2..8a356e6b 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -150,8 +150,11 @@ struct handshake_state {
 	uint8_t r1khid[6];
 	uint8_t gtk[32];
 	uint8_t gtk_rsc[6];
+	uint8_t igtk[32];
+	uint8_t igtk_rsc[6];
 	uint8_t proto_version : 2;
 	unsigned int gtk_index;
+	unsigned int igtk_index;
 	uint8_t active_tk_index;
 	struct erp_cache_entry *erp_cache;
 	bool support_ip_allocation : 1;
@@ -288,6 +291,9 @@ bool handshake_decode_fte_key(struct handshake_state *s, const uint8_t *wrapped,
 void handshake_state_set_gtk(struct handshake_state *s, const uint8_t *key,
 				unsigned int key_index, const uint8_t *rsc);
 
+void handshake_state_set_igtk(struct handshake_state *s, const uint8_t *key,
+				unsigned int key_index, const uint8_t *rsc);
+
 void handshake_state_set_chandef(struct handshake_state *s,
 					struct band_chandef *chandef);
 int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
@@ -307,5 +313,7 @@ const uint8_t *handshake_util_find_pmkid_kde(const uint8_t *data,
 					size_t data_len);
 void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
 					unsigned int key_index, uint8_t *to);
+void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
+					unsigned int key_index, uint8_t *to);
 
 DEFINE_CLEANUP_FUNC(handshake_state_free);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (14 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 15/18] handshake: add functions to save and set IGTK John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 16:20   ` Denis Kenzior
  2024-05-06  0:30 ` [PATCH v2 17/18] ap: generate IGTK on startup if MFP is enabled John Brandt
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When SAE with MFP is being used, include the IGTK in message 3 of the
4-way handshake.
---
 src/eapol.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/eapol.c b/src/eapol.c
index 524a26c9..8b21b886 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -1454,6 +1454,18 @@ static void eapol_send_ptk_3_of_4(struct eapol_sm *sm)
 		key_data_len += gtk_kde[1] + 2;
 	}
 
+	if (sm->handshake->mfp) {
+		enum crypto_cipher group_management_cipher = ie_rsn_cipher_suite_to_cipher(
+				sm->handshake->group_management_cipher);
+		uint8_t *igtk_kde = key_data_buf + key_data_len;
+
+		handshake_util_build_igtk_kde(group_management_cipher,
+						sm->handshake->igtk,
+						sm->handshake->igtk_index,
+						igtk_kde);
+		key_data_len += igtk_kde[1] + 2;
+	}
+
 	if (sm->handshake->support_ip_allocation &&
 			!sm->handshake->client_ip_addr) {
 		handshake_event(sm->handshake, HANDSHAKE_EVENT_P2P_IP_REQUEST);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 17/18] ap: generate IGTK on startup if MFP is enabled
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (15 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-06  0:30 ` [PATCH v2 18/18] ap: propogate IGTK and RSC to handshake John Brandt
  2024-05-07 16:23 ` [PATCH v2 00/18] Basic WPA3 support in AP mode Denis Kenzior
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When using MFP, generate the IGTK group key on startup, and install it
for use. When installing the IGTK, which has either key index 4 or 5,
use the appropriate NL80211 flags so it is installed properly.
---
 src/ap.c          | 61 +++++++++++++++++++++++++++++++++++++++++++----
 src/nl80211util.c |  7 +++++-
 2 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index 8cebef42..f598c173 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -90,6 +90,8 @@ struct ap_state {
 	uint32_t mlme_watch;
 	uint8_t gtk[CRYPTO_MAX_GTK_LEN];
 	uint8_t gtk_index;
+	uint8_t igtk[CRYPTO_MAX_GTK_LEN];
+	uint8_t igtk_index;
 	struct l_queue *wsc_pbc_probes;
 	struct l_timeout *wsc_pbc_timeout;
 	uint16_t wsc_dpid;
@@ -116,6 +118,7 @@ struct ap_state {
 
 	bool started : 1;
 	bool gtk_set : 1;
+	bool igtk_set : 1;
 	bool netconfig_set_addr4 : 1;
 	bool in_event : 1;
 	bool free_pending : 1;
@@ -1656,7 +1659,7 @@ static void ap_start_eap_wsc(struct sta_state *sta)
 	ap_start_handshake(sta, wait_for_eapol_start, NULL);
 }
 
-static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap)
+static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap, uint8_t index)
 {
 	uint32_t ifindex = netdev_get_ifindex(ap->netdev);
 	struct l_genl_msg *msg;
@@ -1665,7 +1668,7 @@ static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap)
 
 	l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
 	l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY);
-	l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &ap->gtk_index);
+	l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &index);
 	l_genl_msg_leave_nested(msg);
 
 	return msg;
@@ -1709,7 +1712,7 @@ static void ap_gtk_op_cb(struct l_genl_msg *msg, void *user_data)
 			cmd == NL80211_CMD_SET_KEY ? "SET_KEY" :
 			"DEL_KEY";
 
-		l_error("%s failed for the GTK: %i",
+		l_error("%s failed for the (I)GTK: %i",
 			cmd_name, l_genl_msg_get_error(msg));
 	}
 }
@@ -1797,6 +1800,39 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
 		ap->gtk_set = true;
 	}
 
+	if (ap->mfpc && !ap->igtk_set) {
+		enum crypto_cipher group_management_cipher =
+			ie_rsn_cipher_suite_to_cipher(ap->group_management_cipher);
+		int igtk_len = crypto_cipher_key_len(group_management_cipher);
+
+		l_getrandom(ap->igtk, igtk_len);
+		ap->igtk_index = 4;
+
+		msg = nl80211_build_new_key_group(
+						netdev_get_ifindex(ap->netdev),
+						group_management_cipher, ap->igtk_index,
+						ap->igtk, igtk_len, NULL,
+						0, NULL);
+
+		if (!l_genl_family_send(ap->nl80211, msg, ap_gtk_op_cb, NULL,
+					NULL)) {
+			l_genl_msg_unref(msg);
+			l_error("Issuing NEW_KEY failed");
+			goto error;
+		}
+
+		msg = nl80211_build_set_key(netdev_get_ifindex(ap->netdev),
+						ap->igtk_index);
+		if (!l_genl_family_send(ap->nl80211, msg, ap_gtk_op_cb, NULL,
+					NULL)) {
+			l_genl_msg_unref(msg);
+			l_error("Issuing SET_KEY failed");
+			goto error;
+		}
+
+		ap->igtk_set = true;
+	}
+
 	if (ap->group_cipher == IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC)
 		ap_start_rsna(sta, NULL);
 	else {
@@ -4137,10 +4173,27 @@ void ap_shutdown(struct ap_state *ap, ap_stopped_func_t stopped_func,
 
 	ap_reset(ap);
 
+	if (ap->igtk_set) {
+		ap->igtk_set = false;
+
+		cmd = ap_build_cmd_del_key(ap, ap->igtk_index);
+		if (!cmd) {
+			l_error("ap_build_cmd_del_key failed");
+			goto free_ap;
+		}
+
+		if (!l_genl_family_send(ap->nl80211, cmd, ap_gtk_op_cb, NULL,
+					NULL)) {
+			l_genl_msg_unref(cmd);
+			l_error("Issuing DEL_KEY failed");
+			goto free_ap;
+		}
+	}
+
 	if (ap->gtk_set) {
 		ap->gtk_set = false;
 
-		cmd = ap_build_cmd_del_key(ap);
+		cmd = ap_build_cmd_del_key(ap, ap->gtk_index);
 		if (!cmd) {
 			l_error("ap_build_cmd_del_key failed");
 			goto free_ap;
diff --git a/src/nl80211util.c b/src/nl80211util.c
index 3f9a43ac..289a73f6 100644
--- a/src/nl80211util.c
+++ b/src/nl80211util.c
@@ -486,14 +486,19 @@ struct l_genl_msg *nl80211_build_set_station_unauthorized(uint32_t ifindex,
 struct l_genl_msg *nl80211_build_set_key(uint32_t ifindex, uint8_t key_index)
 {
 	struct l_genl_msg *msg;
+	int key_type;
 
 	msg = l_genl_msg_new_sized(NL80211_CMD_SET_KEY, 128);
 
 	l_genl_msg_append_attr(msg, NL80211_ATTR_IFINDEX, 4, &ifindex);
 
+	key_type = NL80211_KEY_DEFAULT;
+	if (key_index == 4 || key_index == 5)
+		key_type = NL80211_KEY_DEFAULT_MGMT;
+
 	l_genl_msg_enter_nested(msg, NL80211_ATTR_KEY);
 	l_genl_msg_append_attr(msg, NL80211_KEY_IDX, 1, &key_index);
-	l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT, 0, NULL);
+	l_genl_msg_append_attr(msg, key_type, 0, NULL);
 	l_genl_msg_enter_nested(msg, NL80211_KEY_DEFAULT_TYPES);
 	l_genl_msg_append_attr(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST,
 				0, NULL);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* [PATCH v2 18/18] ap: propogate IGTK and RSC to handshake
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (16 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 17/18] ap: generate IGTK on startup if MFP is enabled John Brandt
@ 2024-05-06  0:30 ` John Brandt
  2024-05-07 16:23 ` [PATCH v2 00/18] Basic WPA3 support in AP mode Denis Kenzior
  18 siblings, 0 replies; 32+ messages in thread
From: John Brandt @ 2024-05-06  0:30 UTC (permalink / raw)
  To: iwd; +Cc: John Brandt

When a client is connecting, remember whether it supports MFP, and if
so, propogate the IGTK to the handshake. Also get the current Receive
Sequence Counter (RSC) of the IGTK and propogate it to the handshake.
---
 src/ap.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 52 insertions(+), 9 deletions(-)

diff --git a/src/ap.c b/src/ap.c
index f598c173..c7ca49e8 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -144,6 +144,7 @@ struct sta_state {
 	struct eapol_sm *sm;
 	struct handshake_state *hs;
 	uint32_t gtk_query_cmd_id;
+	uint8_t prev_igtk_rsc[6];
 	struct l_idle *stop_handshake_work;
 	struct l_settings *wsc_settings;
 	uint8_t wsc_uuid_e[16];
@@ -154,6 +155,7 @@ struct sta_state {
 
 	bool ht_support : 1;
 	bool ht_greenfield : 1;
+	bool mfp : 1;
 };
 
 struct ap_wsc_pbc_probe_record {
@@ -1379,7 +1381,7 @@ static uint32_t ap_send_mgmt_frame(struct ap_state *ap,
 	}))
 
 static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start,
-				const uint8_t *gtk_rsc)
+				const uint8_t *gtk_rsc, const uint8_t *igtk_rsc)
 {
 	struct ap_state *ap = sta->ap;
 	const uint8_t *own_addr = netdev_get_address(ap->netdev);
@@ -1401,6 +1403,9 @@ static void ap_start_handshake(struct sta_state *sta, bool use_eapol_start,
 	if (gtk_rsc)
 		handshake_state_set_gtk(sta->hs, sta->ap->gtk,
 					sta->ap->gtk_index, gtk_rsc);
+	if (sta->mfp && igtk_rsc)
+		handshake_state_set_igtk(sta->hs, sta->ap->igtk,
+					 sta->ap->igtk_index, igtk_rsc);
 
 	if (ap->netconfig_dhcp)
 		sta->hs->support_ip_allocation = true;
@@ -1506,7 +1511,8 @@ static void ap_handshake_event(struct handshake_state *hs,
 	va_end(args);
 }
 
-static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
+static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc,
+			  const uint8_t *igtk_rsc)
 {
 	/* this handshake setup assumes SAE or PSK network */
 	if (sta->hs && sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
@@ -1521,7 +1527,7 @@ static void ap_start_rsna(struct sta_state *sta, const uint8_t *gtk_rsc)
 	handshake_state_set_event_func(sta->hs, ap_handshake_event, sta);
 	handshake_state_set_supplicant_ie(sta->hs, sta->assoc_rsne);
 
-	ap_start_handshake(sta, false, gtk_rsc);
+	ap_start_handshake(sta, false, gtk_rsc, igtk_rsc);
 }
 
 static void ap_gtk_query_cb(struct l_genl_msg *msg, void *user_data)
@@ -1546,13 +1552,48 @@ zero_rsc:
 		gtk_rsc = zero_gtk_rsc;
 	}
 
-	ap_start_rsna(sta, gtk_rsc);
+	ap_start_rsna(sta, gtk_rsc, sta->prev_igtk_rsc);
 	return;
 
 error:
 	ap_del_station(sta, MMPDU_REASON_CODE_UNSPECIFIED, true);
 }
 
+static void ap_igtk_query_cb(struct l_genl_msg *msg, void *user_data)
+{
+	struct sta_state *sta = user_data;
+	struct ap_state *ap = sta->ap;
+
+	const void *igtk_rsc;
+	uint8_t zero_igtk_rsc[6];
+	int err;
+
+	sta->gtk_query_cmd_id = 0;
+
+	err = l_genl_msg_get_error(msg);
+	if (err == -ENOTSUP)
+		goto zero_rsc;
+	else if (err < 0)
+		return;
+
+	igtk_rsc = nl80211_parse_get_key_seq(msg);
+	if (!igtk_rsc) {
+zero_rsc:
+		memset(zero_igtk_rsc, 0, 6);
+		igtk_rsc = zero_igtk_rsc;
+	}
+
+	memcpy(sta->prev_igtk_rsc, igtk_rsc, 6);
+
+	msg = nl80211_build_get_key(netdev_get_ifindex(ap->netdev),
+				    ap->gtk_index);
+	sta->gtk_query_cmd_id = l_genl_family_send(ap->nl80211, msg, ap_gtk_query_cb, sta, NULL);
+	if (!sta->gtk_query_cmd_id) {
+		l_genl_msg_unref(msg);
+		l_error("Issuing GET_KEY failed");
+	}
+}
+
 static void ap_stop_handshake_schedule(struct sta_state *sta)
 {
 	if (sta->stop_handshake_work)
@@ -1656,7 +1697,7 @@ static void ap_start_eap_wsc(struct sta_state *sta)
 	handshake_state_set_event_func(sta->hs, ap_wsc_handshake_event, sta);
 	handshake_state_set_8021x_config(sta->hs, sta->wsc_settings);
 
-	ap_start_handshake(sta, wait_for_eapol_start, NULL);
+	ap_start_handshake(sta, wait_for_eapol_start, NULL, NULL);
 }
 
 static struct l_genl_msg *ap_build_cmd_del_key(struct ap_state *ap, uint8_t index)
@@ -1834,13 +1875,13 @@ static void ap_associate_sta_cb(struct l_genl_msg *msg, void *user_data)
 	}
 
 	if (ap->group_cipher == IE_RSN_CIPHER_SUITE_NO_GROUP_TRAFFIC)
-		ap_start_rsna(sta, NULL);
+		ap_start_rsna(sta, NULL, NULL);
 	else {
 		msg = nl80211_build_get_key(netdev_get_ifindex(ap->netdev),
-					ap->gtk_index);
+					    sta->mfp ? ap->igtk_index : ap->gtk_index);
 		sta->gtk_query_cmd_id = l_genl_family_send(ap->nl80211, msg,
-								ap_gtk_query_cb,
-								sta, NULL);
+							   sta->mfp ? ap_igtk_query_cb : ap_gtk_query_cb,
+							   sta, NULL);
 		if (!sta->gtk_query_cmd_id) {
 			l_genl_msg_unref(msg);
 			l_error("Issuing GET_KEY failed");
@@ -2315,6 +2356,8 @@ static void ap_assoc_reassoc(struct sta_state *sta, bool reassoc,
 			err = MMPDU_REASON_CODE_INVALID_GROUP_CIPHER;
 			goto unsupported;
 		}
+
+		sta->mfp = rsn_info.mfpc && ap->mfpc;
 	}
 
 	/* 802.11-2016 11.3.5.3 j) */
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 04/18] sae: add function sae_set_group
  2024-05-06  0:30 ` [PATCH v2 04/18] sae: add function sae_set_group John Brandt
@ 2024-05-07 14:53   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 14:53 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> Refactor code by adding function sae_set_group. This will make the next
> commits easier where basic SAE support for APs is added.
> ---
>   src/sae.c | 25 ++++++++++++++-----------
>   1 file changed, 14 insertions(+), 11 deletions(-)
> 
> diff --git a/src/sae.c b/src/sae.c
> index bf9fb0ff..4e0b73d8 100644
> --- a/src/sae.c
> +++ b/src/sae.c
> @@ -148,6 +148,18 @@ static void sae_reset_state(struct sae_sm *sm)
>   	sm->pwe = NULL;
>   }
>   
> +static int sae_set_group(struct sae_sm *sm, int group)
> +{
> +	sm->curve = l_ecc_curve_from_ike_group(group);
> +	if (sm->curve == NULL)
> +		return -ENOENT;

Could we avoid side-effects on error by doing something like:

const struct l_ecc_curve *curve = l_ecc_curve_from_ike_group(group);

if (!curve)
	return -ENOENT;

sm->curve = curve;
sm->group = group;
...

> +
> +	sae_debug("Using group %u", group);
> +	sm->group = group;
> +
> +	return 0;
> +}
> +
>   static int sae_choose_next_group(struct sae_sm *sm)
>   {
>   	const unsigned int *ecc_groups = l_ecc_supported_ike_groups();

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config
  2024-05-06  0:30 ` [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config John Brandt
@ 2024-05-07 15:07   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:07 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> On reception of an authentication frame, accept both PSK and SAE as AKM
> depending on the config. Save the client's AKM for later use.
> ---
>   src/ap.c | 29 ++++++++++++++++++++++-------
>   1 file changed, 22 insertions(+), 7 deletions(-)
> 
> diff --git a/src/ap.c b/src/ap.c
> index d50f9e4f..cd253ce3 100644
> --- a/src/ap.c
> +++ b/src/ap.c
> @@ -132,6 +132,7 @@ struct sta_state {
>   	uint8_t *assoc_ies;
>   	size_t assoc_ies_len;
>   	uint8_t *assoc_rsne;
> +	enum ie_rsn_akm_suite akm_suite;
>   	struct eapol_sm *sm;
>   	struct handshake_state *hs;
>   	uint32_t gtk_query_cmd_id;
> @@ -2606,6 +2607,7 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
>   	const uint8_t *from = hdr->address_2;
>   	const uint8_t *bssid = netdev_get_address(ap->netdev);
>   	struct sta_state *sta;
> +	enum ie_rsn_akm_suite akm_suite;
>   
>   	l_info("AP Authentication from %s", util_address_to_string(from));
>   
> @@ -2627,17 +2629,28 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
>   		}
>   	}
>   
> -	/* Only Open System authentication implemented here */
> -	if (L_LE16_TO_CPU(auth->algorithm) !=
> -			MMPDU_AUTH_ALGO_OPEN_SYSTEM) {
> +	if ((ap->akm_suites & IE_RSN_AKM_SUITE_SAE_SHA256) &&
> +	    (L_LE16_TO_CPU(auth->algorithm) == MMPDU_AUTH_ALGO_SAE) ) {
> +		/* When using SAE it must be COMMIT or CONFIRM frame */
> +		if (L_LE16_TO_CPU(auth->transaction_sequence) != 1 &&
> +		    L_LE16_TO_CPU(auth->transaction_sequence) != 2) {

Can we use sae_verify_packet for this?

> +			ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
> +			return;
> +		}
> +		akm_suite = IE_RSN_AKM_SUITE_SAE_SHA256;

nit: doc/coding-style.txt item M1

> +	} else if ((ap->akm_suites & IE_RSN_AKM_SUITE_PSK) &&
> +		   (L_LE16_TO_CPU(auth->algorithm) == MMPDU_AUTH_ALGO_OPEN_SYSTEM) ) {

nit: Only tabs for indentation please

> +		/* When using PSK it must be Open System authentication */
> +		if (L_LE16_TO_CPU(auth->transaction_sequence) != 1) {
> +			ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
> +			return;
> +		}
> +		akm_suite = IE_RSN_AKM_SUITE_PSK;

nit: ditto item M1

> +	} else {
>   		ap_auth_reply(ap, from, MMPDU_REASON_CODE_UNSPECIFIED);
>   		return;
>   	}
>   

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 07/18] sae: verify offered group in AP mode
  2024-05-06  0:30 ` [PATCH v2 07/18] sae: verify offered group " John Brandt
@ 2024-05-07 15:11   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:11 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> When receiving a Commit frame in AP mode, first verify that we support
> the offered group before further processing the frame.
> ---
>   src/sae.c | 15 ++++++++++++++-
>   1 file changed, 14 insertions(+), 1 deletion(-)
> 
> diff --git a/src/sae.c b/src/sae.c
> index 7ba9b0eb..7405a561 100644
> --- a/src/sae.c
> +++ b/src/sae.c
> @@ -216,6 +216,18 @@ static int sae_valid_group(struct sae_sm *sm, unsigned int group)
>   	return -ENOENT;
>   }
>   
> +static int sae_supported_group(struct sae_sm *sm, unsigned int group)
> +{
> +	const unsigned int *ecc_groups = l_ecc_supported_ike_groups();
> +	unsigned int i;
> +
> +	for (i = 0; ecc_groups[i]; i++)
> +		if (ecc_groups[i] == group)
> +			return true;

Function declared as returning int, but you're returning true/false here.

> +
> +	return false;
> +}
> +
>   static bool sae_pwd_seed(const uint8_t *addr1, const uint8_t *addr2,
>   				uint8_t *base, size_t base_len,
>   				uint8_t counter, uint8_t *out)
> @@ -1053,7 +1065,8 @@ static int sae_verify_nothing(struct sae_sm *sm, uint16_t transaction,
>   		return -EBADMSG;
>   
>   	/* reject with unsupported group */
> -	if (l_get_le16(frame) != sm->group)
> +	if ((sm->handshake->authenticator && sae_supported_group(sm, l_get_le16(frame)) < 0) ||

nit: We still use 80 column lines.  This line is way too long.

Also, this if condition will never be true due to sae_supported_group returning 
true/false.

> +	    (!sm->handshake->authenticator && l_get_le16(frame) != sm->group))
>   		return sae_reject(sm, SAE_STATE_COMMITTED,
>   				MMPDU_STATUS_CODE_UNSUPP_FINITE_CYCLIC_GROUP);
>   

Regards,
-Denis

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys
  2024-05-06  0:30 ` [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys John Brandt
@ 2024-05-07 15:13   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:13 UTC (permalink / raw)
  To: John Brandt, iwd

On 5/5/24 7:30 PM, John Brandt wrote:
> Refactor code by moving code to the new function sae_calculate_keys.
> This will make it easier in the next commits to add SAE support for AP
> mode.
> ---
>   src/sae.c | 86 ++++++++++++++++++++++++++++++++-----------------------
>   1 file changed, 50 insertions(+), 36 deletions(-)
> 

<snip>

> @@ -825,6 +791,54 @@ static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
>   	/* don't set the handshakes pmkid until confirm is verified */
>   	memcpy(sm->pmkid, tmp, 16);
>   
> +	return 0;
> +}
> +
> +

nit: No double-empty lines please.  I took this out in my amend.

> +static int sae_process_commit(struct sae_sm *sm, const uint8_t *from,
> +					const uint8_t *frame, size_t len)

Applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 09/18] ap: add support to handle SAE authentication
  2024-05-06  0:30 ` [PATCH v2 09/18] ap: add support to handle SAE authentication John Brandt
@ 2024-05-07 15:44   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:44 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> When the client requests SAE authentication, and it is enabled, allocate
> an auth_proto instance to handle SAE authentication. This also adds a
> new function to send SAE frames in AP mode that can be used by the
> auth_proto instance.
> ---
>   src/ap.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++--------
>   1 file changed, 58 insertions(+), 9 deletions(-)
> 

<snip>

> @@ -2595,6 +2598,38 @@ static void ap_auth_reply(struct ap_state *ap, const uint8_t *dest,
>   				ap_auth_reply_cb, NULL);
>   }
>   
> +static void sae_auth_reply(const uint8_t *data, size_t len, void *user_data)
> +{
> +	struct sta_state *sta = (struct sta_state *)user_data;
> +	struct ap_state *ap = (struct ap_state *)sta->ap;

This cast isn't needed?

> +	const uint8_t *addr = netdev_get_address(ap->netdev);
> +	uint8_t sae_buf[512];
> +	struct mmpdu_header *mpdu = (struct mmpdu_header *) sae_buf;
> +	struct mmpdu_authentication *auth;
> +
> +	if (L_WARN_ON(26 + len > sizeof(sae_buf)))
> +		return;
> +
> +	memset(mpdu, 0, sizeof(*mpdu));
> +
> +	/* Header */
> +	mpdu->fc.protocol_version = 0;
> +	mpdu->fc.type = MPDU_TYPE_MANAGEMENT;
> +	mpdu->fc.subtype = MPDU_MANAGEMENT_SUBTYPE_AUTHENTICATION;
> +	memcpy(mpdu->address_1, sta->addr, 6);	/* DA */
> +	memcpy(mpdu->address_2, addr, 6);	/* SA */
> +	memcpy(mpdu->address_3, addr, 6);	/* BSSID */
> +
> +	/* Authentication body */
> +	auth = (void *) mmpdu_body(mpdu);
> +	auth->algorithm = L_CPU_TO_LE16(MMPDU_AUTH_ALGO_SAE);
> +
> +	/* SAE elements */
> +	memcpy(sae_buf + 26, data, len);
> +	ap_send_mgmt_frame(ap, mpdu, 26 + len,
> +				ap_auth_reply_cb, NULL);
> +}
> +
>   /*
>    * 802.11-2016 9.3.3.12 (frame format), 802.11-2016 11.3.4.3 and
>    * 802.11-2016 12.3.3.2 (MLME/SME)

<snip>

> @@ -2680,18 +2715,32 @@ static void ap_auth_cb(const struct mmpdu_header *hdr, const void *body,
>   		ap->sta_states = l_queue_new();
>   
>   	sta->akm_suite = akm_suite;
> +	if (sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
> +		sta->hs = netdev_handshake_state_new(sta->ap->netdev);
> +		handshake_state_set_authenticator(sta->hs, true);
> +		handshake_state_set_passphrase(sta->hs, ap->passphrase);
> +		handshake_state_set_supplicant_address(sta->hs, sta->addr);
> +		handshake_state_set_authenticator_address(sta->hs, bssid);
> +
> +		sta->auth_proto = sae_sm_new(sta->hs, sae_auth_reply, NULL, sta);
> +	}
>   
>   	l_queue_push_tail(ap->sta_states, sta);
>   
> -	/*
> -	 * Nothing to do here netlink-wise as we can't receive any data
> -	 * frames until after association anyway.  We do need to add a
> -	 * timeout for the authentication and possibly the kernel could
> -	 * handle that if we registered the STA with NEW_STATION now (TODO)
> -	 */
>   
> -done:
> -	ap_auth_reply(ap, from, 0);
> +have_sta:
> +	if (sta->akm_suite == IE_RSN_AKM_SUITE_SAE_SHA256) {
> +		auth_proto_rx_authenticate(sta->auth_proto, (const uint8_t *)hdr,
> +					   body + body_len - (void *) hdr);

nit: This line is also > 80 chars.

To be more future proof, maybe:

if (sta->auth_proto) {
	auth_proto_rx_authenticate(...);
	return;
}

/* comment */
ap_auth_reply(...);

> +	} else {
> +		/*
> +		 * Nothing to do here netlink-wise as we can't receive any data
> +		 * frames until after association anyway.  We do need to add a
> +		 * timeout for the authentication and possibly the kernel could
> +		 * handle that if we registered the STA with NEW_STATION now (TODO)
> +		 */
> +		ap_auth_reply(ap, from, 0);
> +	}
>   }
>   
>   /* 802.11-2016 9.3.3.13 (frame format), 802.11-2016 11.3.4.5 (MLME/SME) */

Regards,
-Denis

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 03/18] unit: fix SAE unit tests
  2024-05-06  0:30 ` [PATCH v2 03/18] unit: fix SAE unit tests John Brandt
@ 2024-05-07 15:51   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:51 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> Don't mark either client as being the authenticator. In the current unit
> tests, both instances act as clients to test functionality. This ensures
> the unit does not show an error during the following commits where SAE
> for AP mode is added.
> ---
>   unit/test-sae.c | 3 ---
>   1 file changed, 3 deletions(-)
> 

Applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 08/18] sae: support reception of Confirm frame by AP
  2024-05-06  0:30 ` [PATCH v2 08/18] sae: support reception of Confirm frame by AP John Brandt
@ 2024-05-07 15:51   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:51 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> Experimental AP-mode support for receiving a Confirm frame when in the
> COMMITTED state. The AP will reply with a Confirm frame.
> 
> Note that when acting as an AP, on reception of a Commit frame, the AP
> only replies with a Commit frame. The protocols allows to also already
> send the Confirm frame, but older clients may not support simultaneously
> receiving a Commit and Confirm frame.
> ---
>   src/sae.c | 48 +++++++++++++++++++++++++++++++++++-------------
>   1 file changed, 35 insertions(+), 13 deletions(-)
> 

I went ahead and applied this patch as well after re-flowing some lines that 
were > 80 characters.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 11/18] eapol: support PTK derivation with SHA256
  2024-05-06  0:30 ` [PATCH v2 11/18] eapol: support PTK derivation with SHA256 John Brandt
@ 2024-05-07 15:52   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 15:52 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> Support PTK derivation in case the negotiated AKM requires SHA256. This
> is needed to support SAE in AP mode.
> ---
>   src/eapol.c | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 

Applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers
  2024-05-06  0:30 ` [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers John Brandt
@ 2024-05-07 16:04   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 16:04 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> Support encrypting key data when the cipher is AKM-defined. This is
> needed to support SAE in AP mode.
> ---
>   src/eapol.c | 51 ++++++++++++++++++++++++++++++++++++---------------
>   1 file changed, 36 insertions(+), 15 deletions(-)
> 

Applied, after amending some > 80 char lines.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 14/18] ap: move toward requiring MFP when using SAE
  2024-05-06  0:30 ` [PATCH v2 14/18] ap: move toward requiring MFP when using SAE John Brandt
@ 2024-05-07 16:12   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 16:12 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> When wanting to use SAE, confirm that MFP is also supported, and
> automatically enable MFP. Advertise as MFP capable in the beacon.
> ---
>   src/ap.c    | 13 +++++++++++--
>   src/wiphy.c |  2 +-
>   src/wiphy.h |  2 ++
>   3 files changed, 14 insertions(+), 3 deletions(-)
> 
> diff --git a/src/ap.c b/src/ap.c
> index ae406e16..8cebef42 100644
> --- a/src/ap.c
> +++ b/src/ap.c
> @@ -82,6 +82,7 @@ struct ap_state {
>   
>   	unsigned int ciphers;
>   	enum ie_rsn_cipher_suite group_cipher;
> +	enum ie_rsn_cipher_suite group_management_cipher;
>   	unsigned int akm_suites;
>   	uint32_t beacon_interval;
>   	struct l_uintset *rates;
> @@ -93,6 +94,7 @@ struct ap_state {
>   	struct l_timeout *wsc_pbc_timeout;
>   	uint16_t wsc_dpid;
>   	uint8_t wsc_uuid_r[16];
> +	bool mfpc;
>   
>   	uint16_t last_aid;
>   	struct l_queue *sta_states;
> @@ -639,6 +641,9 @@ static void ap_set_rsn_info(struct ap_state *ap, struct ie_rsn_info *rsn)
>   	rsn->akm_suites = ap->akm_suites;
>   	rsn->pairwise_ciphers = ap->ciphers;
>   	rsn->group_cipher = ap->group_cipher;
> +
> +	rsn->group_management_cipher = ap->group_management_cipher;
> +	rsn->mfpc = ap->mfpc;
>   }
>   
>   static void ap_wsc_exit_pbc(struct ap_state *ap)
> @@ -3916,9 +3921,13 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
>   	for (i = 0; akms_str && akms_str[i]; i++) {
>   		if (!strcmp(akms_str[i], "PSK"))
>   			ap->akm_suites |= IE_RSN_AKM_SUITE_PSK;
> -		else if (!strcmp(akms_str[i], "SAE"))
> +		else if (!strcmp(akms_str[i], "SAE")) {
> +			if (!wiphy_can_connect_sae(wiphy))

wiphy_can_connect_sae checks NL80211_FEATURE_SAE and 
NL80211_EXT_FEATURE_SAE_OFFLOAD bit, which is for clients only.  The AP 
equivalent is NL80211_EXT_FEATURE_SAE_OFFLOAD_AP.  Refer to linux/nl80211.h for 
more details.  You're probably better off using wiphy_get_supported_ciphers instead.

> +				return -ENOTSUP;
>   			ap->akm_suites |= IE_RSN_AKM_SUITE_SAE_SHA256;
> -		else {
> +			ap->group_management_cipher = IE_RSN_CIPHER_SUITE_BIP_CMAC;
> +			ap->mfpc = true;
> +		} else {
>   			l_warn("Unsupported or unknown AKM suite %s",
>   					akms_str[i]);
>   			return -ENOTSUP;

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 15/18] handshake: add functions to save and set IGTK
  2024-05-06  0:30 ` [PATCH v2 15/18] handshake: add functions to save and set IGTK John Brandt
@ 2024-05-07 16:20   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 16:20 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> To add MFP support in the AP mode, add utility functions to save the
> IGTK and to add the IGTK to handshake messages.
> ---
>   src/handshake.c | 34 ++++++++++++++++++++++++++++++++++
>   src/handshake.h |  8 ++++++++
>   2 files changed, 42 insertions(+)
> 

Applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP
  2024-05-06  0:30 ` [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP John Brandt
@ 2024-05-07 16:20   ` Denis Kenzior
  0 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 16:20 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> When SAE with MFP is being used, include the IGTK in message 3 of the
> 4-way handshake.
> ---
>   src/eapol.c | 12 ++++++++++++
>   1 file changed, 12 insertions(+)
> 

Applied after amending > 80 char line.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 32+ messages in thread

* Re: [PATCH v2 00/18] Basic WPA3 support in AP mode
  2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
                   ` (17 preceding siblings ...)
  2024-05-06  0:30 ` [PATCH v2 18/18] ap: propogate IGTK and RSC to handshake John Brandt
@ 2024-05-07 16:23 ` Denis Kenzior
  18 siblings, 0 replies; 32+ messages in thread
From: Denis Kenzior @ 2024-05-07 16:23 UTC (permalink / raw)
  To: John Brandt, iwd

Hi John,

On 5/5/24 7:30 PM, John Brandt wrote:
> This set of patches adds basic WPA3 support for IWD in AP mode. It has
> been tested by connecting to IWD AP using wpa_supplicant, both when WPA3
> is enabled and when it was not. A unit test for SAE mode is now also
> included and all other unit tests now pass again.
> 
> Compared to the previous version, this patch now also includes MFP
> support for AP mode. The AP will generate an IGTK on startup, and
> distribute it to MFP-capable clients. Sanity checks on received SAE
> frames are now also added.

Very nice series.  Thanks for doing this.  I applied what looked good and seemed 
safe to apply.  For the rest, please send a v3.

Thanks,
-Denis

^ permalink raw reply	[flat|nested] 32+ messages in thread

end of thread, other threads:[~2024-05-07 16:23 UTC | newest]

Thread overview: 32+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-06  0:30 [PATCH v2 00/18] Basic WPA3 support in AP mode John Brandt
2024-05-06  0:30 ` [PATCH v2 01/18] ap: ability to advertise PSK and SAE John Brandt
2024-05-06  0:30 ` [PATCH v2 02/18] ap: accept PSK/SAE in auth depending on config John Brandt
2024-05-07 15:07   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 03/18] unit: fix SAE unit tests John Brandt
2024-05-07 15:51   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 04/18] sae: add function sae_set_group John Brandt
2024-05-07 14:53   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 05/18] sae: refactor and add function sae_calculate_keys John Brandt
2024-05-07 15:13   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 06/18] sae: make sae_process_commit callable in AP mode John Brandt
2024-05-06  0:30 ` [PATCH v2 07/18] sae: verify offered group " John Brandt
2024-05-07 15:11   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 08/18] sae: support reception of Confirm frame by AP John Brandt
2024-05-07 15:51   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 09/18] ap: add support to handle SAE authentication John Brandt
2024-05-07 15:44   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 10/18] ap: enable start of 4-way HS after SAE John Brandt
2024-05-06  0:30 ` [PATCH v2 11/18] eapol: support PTK derivation with SHA256 John Brandt
2024-05-07 15:52   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 12/18] eapol: encrypt key data for AKM-defined ciphers John Brandt
2024-05-07 16:04   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 13/18] unit: add unit test for SAE AP mode John Brandt
2024-05-06  0:30 ` [PATCH v2 14/18] ap: move toward requiring MFP when using SAE John Brandt
2024-05-07 16:12   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 15/18] handshake: add functions to save and set IGTK John Brandt
2024-05-07 16:20   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 16/18] eapol: include IGTK in 4-way handshake as AP John Brandt
2024-05-07 16:20   ` Denis Kenzior
2024-05-06  0:30 ` [PATCH v2 17/18] ap: generate IGTK on startup if MFP is enabled John Brandt
2024-05-06  0:30 ` [PATCH v2 18/18] ap: propogate IGTK and RSC to handshake John Brandt
2024-05-07 16:23 ` [PATCH v2 00/18] Basic WPA3 support in AP mode Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).