[RFC v2 37/99] NAN: Add support for NDP security Rx flow

Andrei Otcheretianski andrei.otcheretianski at intel.com
Tue Dec 23 03:51:41 PST 2025


From: Ilan Peer <ilan.peer at intel.com>

Add Rx flow processing and state for NDP security attributes.
Currently support only shared key flows.

Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
 src/nan/Makefile        |   9 +-
 src/nan/nan_i.h         |  53 ++++
 src/nan/nan_sec.c       | 590 ++++++++++++++++++++++++++++++++++++++++
 wpa_supplicant/Makefile |   1 +
 4 files changed, 652 insertions(+), 1 deletion(-)
 create mode 100644 src/nan/nan_sec.c

diff --git a/src/nan/Makefile b/src/nan/Makefile
index 7d79a2d77e..ae7879553a 100644
--- a/src/nan/Makefile
+++ b/src/nan/Makefile
@@ -1,2 +1,9 @@
-LIB_OBJS= nan.o nan_util.o nan_ndp.o nan_ndl.o nan_crypto.o
+LIB_OBJS= \
+	  nan.o \
+	  nan_util.o \
+	  nan_ndp.o \
+	  nan_ndl.o \
+	  nan_crypto.o \
+	  nan_sec.o
+
 include ../lib.rules
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 23a153dde9..1d46a70d71 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -43,6 +43,54 @@ struct nan_ptk {
 	size_t tk_len;
 };
 
+/*
+ * struct nan_ndp_sec - NAN ndp security state
+ *
+ * @present: True iff NDP setup exchange includes security
+ * @valid: True iff the security configuration is valid
+ * @replaycnt_ok: True iff replay count ok
+ * @replaycnt: Current replay count
+ * @i_nonce: Initiator nonce
+ * @i_capab: Initiator capabilities
+ * @i_csid: Initiator cipher suite ID
+ * @i_instance_id: Initiator publish instance ID
+ * @i_pmkid: Initiator PMKID
+ * @r_nonce: Responder nonce
+ * @r_capab: Responder capabilities
+ * @r_csid: Responder cipher suite ID
+ * @r_instance_id: Responder instance ID
+ * @r_pmkid: Responder PMKID
+ * @auth_token: Authentication token
+ * @pmk: PMK used for the secure NDP establishment
+ * @ptk: Derived PTK
+ */
+struct nan_ndp_sec {
+	bool present;
+	bool valid;
+
+	bool replaycnt_ok;
+	u8 replaycnt[8];
+
+	/* Initiator data */
+	u8 i_nonce[WPA_NONCE_LEN];
+	u8 i_capab;
+	u8 i_csid;
+	u8 i_instance_id;
+	u8 i_pmkid[PMKID_LEN];
+
+	/* Responder data */
+	u8 r_nonce[WPA_NONCE_LEN];
+	u8 r_capab;
+	u8 r_csid;
+	u8 r_instance_id;
+	u8 r_pmkid[PMKID_LEN];
+
+	u8 auth_token[NAN_AUTH_TOKEN_LEN];
+	u8 pmk[PMK_LEN];
+
+	struct nan_ptk ptk;
+};
+
 /*
  * enum nan_ndp_state - State of NDP establishment
  * @NAN_NDP_STATE_NONE: No NDP establishment in progress.
@@ -112,6 +160,7 @@ struct nan_ndp {
  * @ssi: Service specific information
  * @ssi_len: Service specific information length
  * @service_id: Service ID of the service used for NDP setup
+ * @sec: NDP security data
  */
 struct nan_ndp_setup {
 	struct nan_ndp *ndp;
@@ -125,6 +174,7 @@ struct nan_ndp_setup {
 	u16 ssi_len;
 
 	u8 service_id[NAN_SERVICE_ID_LEN];
+	struct nan_ndp_sec sec;
 };
 
 /*
@@ -504,4 +554,7 @@ int nan_crypto_calc_auth_token(enum nan_cipher_suite_id cipher,
 			       const u8 *buf, size_t len, u8 *token);
 int nan_crypto_key_mic(const u8 *buf, size_t len, const u8 *kck,
 		       size_t kck_len, u8 cipher, u8 *mic);
+void nan_sec_reset(struct nan_data *nan, struct nan_ndp_sec *ndp_sec);
+int nan_sec_rx(struct nan_data *nan, struct nan_peer *peer,
+	       struct nan_msg *msg);
 #endif
diff --git a/src/nan/nan_sec.c b/src/nan/nan_sec.c
new file mode 100644
index 0000000000..1fb4a48d3c
--- /dev/null
+++ b/src/nan/nan_sec.c
@@ -0,0 +1,590 @@
+/*
+ * Wi-Fi Aware - NAN Data link security
+ * Copyright (C) 2025 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include "utils/common.h"
+#include "common/ieee802_11_common.h"
+#include "nan_i.h"
+
+/*
+ * nan_sec_reset - Reset security state
+ *
+ * @nan: NAN module context from nan_init()
+ * @ndp_sec: NDP security context to reset
+ */
+void nan_sec_reset(struct nan_data *nan, struct nan_ndp_sec *ndp_sec)
+{
+	os_memset(ndp_sec, 0, sizeof(*ndp_sec));
+}
+
+
+static void nan_sec_dump(struct nan_data *nan, struct nan_peer *peer)
+{
+	struct nan_ndp_sec *s = &peer->ndp_setup.sec;
+
+	wpa_printf(MSG_DEBUG,
+		   "NAN: SEC: present=%u, valid=%u",
+		   s->present, s->valid);
+	wpa_printf(MSG_DEBUG,
+		   "NAN: SEC: i_csid=%u, i_instance_id=%u",
+		   s->i_csid, s->i_instance_id);
+	wpa_printf(MSG_DEBUG,
+		   "NAN: SEC: r_csid=%u, r_instance_id=%u",
+		   s->r_csid, s->r_instance_id);
+}
+
+
+static int nan_sec_rx_m1_verify(struct nan_data *nan,
+				const struct nan_attrs *attrs)
+{
+	struct nan_sec_ctxt *sc;
+	u16 expected_len;
+
+	expected_len = sizeof(struct nan_cipher_suite_info) +
+		sizeof(struct nan_cipher_suite);
+
+	if (!attrs->cipher_suite_info  ||
+	    attrs->cipher_suite_info_len < expected_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Missing valid cipher suite attribute");
+		return -1;
+	}
+
+	/* Need at least one PMKID */
+	expected_len = sizeof(struct nan_sec_ctxt) + PMKID_LEN;
+
+	if (!attrs->sec_ctxt_info  || attrs->sec_ctxt_info_len < expected_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Missing valid security context attribute");
+		return -1;
+	}
+
+	sc = (struct nan_sec_ctxt *)attrs->sec_ctxt_info;
+	if (sc->scid != NAN_SEC_CTX_TYPE_PMKID ||
+	    le_to_host16(sc->len) != PMKID_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Got unknown security context");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int nan_sec_rx_m1(struct nan_data *nan, struct nan_peer *peer,
+			 const struct nan_msg *msg,
+			 const struct wpa_eapol_key *key)
+{
+	struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec;
+	struct nan_cipher_suite_info *cs_info;
+	struct nan_cipher_suite *cs;
+	struct nan_sec_ctxt *sc;
+	u8 instance_id;
+	int ret;
+
+	if (peer->ndp_setup.state != NAN_NDP_STATE_REQ_RECV) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m1");
+		return -1;
+	}
+
+	ret = nan_sec_rx_m1_verify(nan, &msg->attrs);
+	if (ret)
+		return ret;
+
+	cs_info = (struct nan_cipher_suite_info *)msg->attrs.cipher_suite_info;
+	cs = (struct nan_cipher_suite *)cs_info->cs;
+	sc = (struct nan_sec_ctxt *)msg->attrs.sec_ctxt_info;
+
+	/* Get the cipher suites data */
+	ndp_sec->i_capab = cs_info->capab;
+	ndp_sec->i_csid = cs->csid;
+	ndp_sec->i_instance_id = cs->instance_id;
+
+	instance_id = sc->instance_id;
+
+	if (ndp_sec->i_instance_id && instance_id &&
+	    ndp_sec->i_instance_id != instance_id) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Mismatched instance ID");
+		return -1;
+	}
+
+	os_memcpy(ndp_sec->i_pmkid, sc->ctxt, PMKID_LEN);
+
+	/* Save initiator's nonce */
+	os_memcpy(ndp_sec->i_nonce, key->key_nonce, WPA_NONCE_LEN);
+
+	/* Save the replay counter */
+	os_memcpy(ndp_sec->replaycnt, key->replay_counter,
+		  sizeof(key->replay_counter));
+
+	/* Store the authentication token, that is required for m3 MIC */
+	ret = nan_crypto_calc_auth_token(ndp_sec->i_csid, (u8 *)&msg->mgmt->u,
+					 msg->len - 24, ndp_sec->auth_token);
+	if (ret)
+		return ret;
+
+	/* The flow will continue, once higher layer ACK the NDP setup */
+	ndp_sec->valid = 1;
+	return 0;
+}
+
+
+static int nan_sec_key_mic_ver(struct nan_data *nan, u8 *buf, size_t len,
+			       const struct wpa_eapol_key *key,
+			       u8 *kck, size_t kck_len, u8 csid)
+{
+	u8 mic[NAN_KEY_MIC_24_LEN];
+	u8 *pos = (u8 *)(key + 1);
+	u8 mic_len;
+	int ret;
+
+	os_memset(mic, 0, sizeof(mic));
+
+	if (csid == NAN_CS_SK_CCM_128)
+		mic_len = NAN_KEY_MIC_LEN;
+	else if (csid == NAN_CS_SK_GCM_256)
+		mic_len = NAN_KEY_MIC_24_LEN;
+	else
+		return -1;
+
+	os_memcpy(mic, pos, mic_len);
+	os_memset(pos, 0, mic_len);
+
+	ret = nan_crypto_key_mic(buf, len, kck, kck_len, csid, pos);
+	if (ret)
+		return ret;
+
+	if (os_memcmp_const(mic, pos, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: MIC verification failed");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+static int nan_sec_rx_m2(struct nan_data *nan, struct nan_peer *peer,
+			 const struct nan_msg *msg,
+			 const struct wpa_eapol_key *key)
+{
+	struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec;
+	struct nan_ndp *ndp = peer->ndp_setup.ndp;
+	struct nan_cipher_suite_info *cs_info;
+	struct nan_cipher_suite *cs;
+	struct nan_ptk tptk;
+	u16 cs_info_len;
+	u8 *pos;
+	int ret;
+
+	if (peer->ndp_setup.state != NAN_NDP_STATE_RES_RECV) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m2");
+		return -1;
+	}
+
+	cs_info = (struct nan_cipher_suite_info *)msg->attrs.cipher_suite_info;
+
+	cs_info_len = sizeof(struct nan_cipher_suite_info) +
+		sizeof(struct nan_cipher_suite);
+
+	if (!cs_info || msg->attrs.cipher_suite_info_len < cs_info_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Invalid cipher suite attribute");
+		return -1;
+	}
+
+	cs = (struct nan_cipher_suite *)cs_info->cs;
+
+	/* Get the cipher suites data */
+	ndp_sec->r_capab = cs_info->capab;
+	ndp_sec->r_csid = cs->csid;
+	ndp_sec->r_instance_id = cs->instance_id;
+
+	if (ndp_sec->i_csid != ndp_sec->r_csid) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: i_csid != r_csid (%u, %u)",
+			   ndp_sec->i_csid, ndp_sec->r_csid);
+		return -1;
+	}
+
+	/* Save responder's nonce */
+	os_memcpy(ndp_sec->r_nonce, key->key_nonce, WPA_NONCE_LEN);
+
+	/* Note: replay counter should have been verified by caller */
+
+	/* PTK should be derived using NDI addresses */
+	ret = nan_crypto_pmk_to_ptk(ndp_sec->pmk,
+				    ndp->init_ndi, ndp->resp_ndi,
+				    ndp_sec->i_nonce, ndp_sec->r_nonce,
+				    &tptk, ndp_sec->i_csid);
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: m2 failed to derive PTK");
+		return ret;
+	}
+
+	/*
+	 * Due to the different MIC size, need to handle the fields starting
+	 * with the mic differently
+	 */
+	pos = (u8 *)(key + 1);
+	if (ndp_sec->i_csid == NAN_CS_SK_CCM_128)
+		pos += NAN_KEY_MIC_LEN;
+	else
+		pos += NAN_KEY_MIC_24_LEN;
+
+	if (WPA_GET_BE16(pos))
+		wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m2 key data");
+
+	/* Verify MIC */
+	ret = nan_sec_key_mic_ver(nan, (u8 *)&msg->mgmt->u,
+				  msg->len - 24, key,
+				  tptk.kck, tptk.kck_len,
+				  ndp_sec->i_csid);
+	if (ret)
+		return ret;
+
+	/* MIC is verified; save PTK */
+	os_memcpy(&ndp_sec->ptk, &tptk, sizeof(tptk));
+	forced_memzero(&tptk, sizeof(tptk));
+
+	/* Increment the replay counter here to prevent replays */
+	WPA_PUT_BE64(ndp_sec->replaycnt,
+		     (WPA_GET_BE64(ndp_sec->replaycnt) + 1));
+	return 0;
+}
+
+
+static int nan_sec_rx_m3(struct nan_data *nan, struct nan_peer *peer,
+			 const struct nan_msg *msg,
+			 const struct wpa_eapol_key *key)
+{
+	struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec;
+	u8 mic[NAN_KEY_MIC_24_LEN];
+	u8 *buf;
+	u8 mic_len;
+	u8 *pos;
+	int ret;
+
+	if (peer->ndp_setup.state != NAN_NDP_STATE_CON_RECV) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m3");
+		return -1;
+	}
+
+	/* Note: replay counter should have been verified by caller */
+
+	/* Verify the i_nonce did not change */
+	if (os_memcmp(key->key_nonce, ndp_sec->i_nonce, WPA_NONCE_LEN) != 0) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: m3 key nonce mismatch");
+		return -1;
+	}
+
+	/*
+	 * In case of m3, the MIC is calculated over the frame body
+	 * concatenated with the authentication token
+	 */
+	buf = os_malloc(msg->len - 24 + NAN_AUTH_TOKEN_LEN);
+	if (!buf)
+		return -ENOBUFS;
+
+	/*
+	 * Due to the different MIC size, need to handle the fields starting
+	 * with the mic differently
+	 */
+	pos = (u8 *)(key + 1);
+	if (ndp_sec->i_csid == NAN_CS_SK_CCM_128)
+		mic_len = NAN_KEY_MIC_LEN;
+	else
+		mic_len = NAN_KEY_MIC_24_LEN;
+
+	if (WPA_GET_BE16(pos + mic_len))
+		wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m3 key data");
+
+	/* Copy MIC and os_memset it */
+	os_memcpy(mic, pos, mic_len);
+	os_memset(pos, 0, mic_len);
+	os_memcpy(buf, ndp_sec->auth_token, NAN_AUTH_TOKEN_LEN);
+	os_memcpy(buf + NAN_AUTH_TOKEN_LEN, (u8 *)&msg->mgmt->u, msg->len - 24);
+
+	ret = nan_crypto_key_mic(buf, msg->len - 24 + NAN_AUTH_TOKEN_LEN,
+				 ndp_sec->ptk.kck,
+				 ndp_sec->ptk.kck_len,
+				 ndp_sec->i_csid,
+				 pos);
+	os_free(buf);
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: m3 MIC calculation failed");
+		return ret;
+	}
+
+	if (os_memcmp_const(mic, pos, mic_len) != 0) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: m3 MIC verification failed");
+		return -1;
+	}
+
+	forced_memzero(mic, sizeof(mic));
+
+	/* Save replay counter */
+	os_memcpy(ndp_sec->replaycnt, key->replay_counter,
+		  sizeof(key->replay_counter));
+	return 0;
+}
+
+
+static int nan_sec_rx_m4(struct nan_data *nan, struct nan_peer *peer,
+			 const struct nan_msg *msg,
+			 const struct wpa_eapol_key *key)
+{
+	struct nan_ndp_sec *ndp_sec = &peer->ndp_setup.sec;
+	u8 *pos = (u8 *)(key + 1);
+	int ret;
+
+	if (peer->ndp_setup.state != NAN_NDP_STATE_DONE) {
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Not expecting m4");
+		return -1;
+	}
+
+	/* Note: replay counter should have been verified by caller */
+
+	/*
+	 * Due to the different MIC size, need to handle the fields starting
+	 * with the mic differently
+	 */
+	if (ndp_sec->i_csid == NAN_CS_SK_CCM_128)
+		pos += NAN_KEY_MIC_LEN;
+	else
+		pos += NAN_KEY_MIC_24_LEN;
+
+	if (WPA_GET_BE16(pos))
+		wpa_printf(MSG_DEBUG, "NAN: SEC: TODO: m4 key data");
+
+	/* Verify MIC */
+	ret = nan_sec_key_mic_ver(nan, (u8 *)&msg->mgmt->u,
+				  msg->len - 24, key,
+				  ndp_sec->ptk.kck,
+				  ndp_sec->ptk.kck_len,
+				  ndp_sec->i_csid);
+	if (ret)
+		return ret;
+
+	/* Increment the replay counter here to prevent replays */
+	WPA_PUT_BE64(ndp_sec->replaycnt,
+		     (WPA_GET_BE64(ndp_sec->replaycnt) + 1));
+
+	/* Security negotiation done */
+	return 0;
+}
+
+
+/*
+ * nan_sec_rx - Handle security context for Rx frames
+ *
+ * @nan: NAN module context from nan_init()
+ * @peer: Peer from which the original message was received
+ * @msg: Parsed nan action frame
+ * Returns: 0 on success, negative on failure
+ */
+int nan_sec_rx(struct nan_data *nan, struct nan_peer *peer,
+	       struct nan_msg *msg)
+{
+	struct nan_ndp_setup *ndp_setup = &peer->ndp_setup;
+	struct nan_ndp_sec *ndp_sec = &ndp_setup->sec;
+	struct wpa_eapol_key *key;
+	struct nan_shared_key *shared_key_desc;
+	u16 info, total_len, desc, shared_key_desc_len;
+	u8 instance_id, cipher;
+	u8 *pos;
+	int ret;
+
+	wpa_printf(MSG_DEBUG, "NAN: SEC: NDP security Rx");
+	nan_sec_dump(nan, peer);
+
+	if (!ndp_sec->present) {
+		if (!ndp_sec->valid)
+			return 0;
+
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: NDP with security but present=0");
+		return -1;
+	}
+
+	shared_key_desc = (struct nan_shared_key *)msg->attrs.shared_key_desc;
+	shared_key_desc_len = msg->attrs.shared_key_desc_len;
+
+	/* Shared key descriptor mandatory in all messages */
+	if (!shared_key_desc || !shared_key_desc_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: No shared key descriptor attribute");
+		return -1;
+	}
+
+	/*
+	 * As the shared key type depends on the cipher suite negotiated, need
+	 * to get the cipher suite to validate the proper length of the
+	 * descriptor.
+	 */
+	if (ndp_sec->valid) {
+		cipher = ndp_sec->i_csid;
+	} else {
+		struct nan_cipher_suite_info *cs_info;
+		struct nan_cipher_suite *cs;
+
+		/* Exactly one cipher suite should be negotiated */
+		if (!msg->attrs.cipher_suite_info ||
+		    msg->attrs.cipher_suite_info_len !=
+		    (sizeof(struct nan_cipher_suite_info) +
+		     sizeof(struct nan_cipher_suite))) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Missing/bad cipher suite attribute");
+			return -1;
+		}
+
+		cs_info = (struct nan_cipher_suite_info *)
+			msg->attrs.cipher_suite_info;
+		cs = (struct nan_cipher_suite *)cs_info->cs;
+
+		cipher = cs->csid;
+		if (cipher != NAN_CS_SK_CCM_128 &&
+		    cipher != NAN_CS_SK_GCM_256) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Unsupported cipher suite=%u",
+				   cipher);
+			return -1;
+		}
+	}
+
+	key = (struct wpa_eapol_key *)shared_key_desc->key;
+	pos = (u8 *)(key + 1);
+
+	total_len = sizeof(*key) + 2;
+
+	if (cipher == NAN_CS_SK_CCM_128) {
+		if (shared_key_desc_len <
+		    (sizeof(struct nan_shared_key) + sizeof(*key) +
+		     NAN_KEY_MIC_LEN + 2)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Shared key descriptor too small");
+			return -1;
+		}
+
+		total_len += NAN_KEY_MIC_LEN +
+			WPA_GET_BE16(pos + NAN_KEY_MIC_LEN);
+
+		if (total_len >
+		    (shared_key_desc_len - sizeof(struct nan_shared_key))) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Invalid shared key: payload len");
+			return -1;
+		}
+	} else {
+		if (shared_key_desc_len <
+		    (sizeof(struct nan_shared_key) + sizeof(*key) +
+		     NAN_KEY_MIC_24_LEN + 2)) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Shared key (24) descriptor too small");
+			return -1;
+		}
+
+		total_len += NAN_KEY_MIC_24_LEN +
+			WPA_GET_BE16(pos + NAN_KEY_MIC_24_LEN);
+
+		if (total_len >
+		    (shared_key_desc_len - sizeof(struct nan_shared_key))) {
+			wpa_printf(MSG_DEBUG,
+				   "NAN: SEC: Invalid shared key (24): payload len");
+			return -1;
+		}
+	}
+
+	/*
+	 * Note: only the fields before the mic are accessed in the function, so
+	 * it is safe to continue with the key pointer.
+	 */
+
+	/* Note: according to the NAN specification the following fields should
+	 * be ignored:
+	 * key->len: as the key length is derived from the cipher suite.
+	 * key->iv: not needed for AES Key WRAP
+	 * key->rsc: to avoid implicit assumption of a single GTK.
+	 */
+	if (key->type != NAN_KEY_DESC) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Invalid shared key: key descriptor=0x%x",
+			   key->type);
+		return -1;
+	}
+
+	info = WPA_GET_BE16(key->key_info);
+
+	/* Discard EAPOL-Key frames with an unknown descriptor version */
+	desc = info & WPA_KEY_INFO_TYPE_MASK;
+	if (desc != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Invalid shared key: invalid key version=0x%x",
+			   desc);
+		return -1;
+	}
+
+	if (ndp_sec->replaycnt_ok &&
+	    WPA_GET_BE64(key->replay_counter) <
+	    WPA_GET_BE64(ndp_sec->replaycnt)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Replay counter did not increase");
+		return -1;
+	}
+
+	if (info & WPA_KEY_INFO_REQUEST) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Invalid shared key: key request not supported");
+		return -1;
+	} else if (!(info & WPA_KEY_INFO_KEY_TYPE)) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Invalid shared key: group handshake not supported");
+		return -1;
+	}
+
+	switch (msg->oui_subtype) {
+	case NAN_SUBTYPE_DATA_PATH_REQUEST:
+		if (!(info & WPA_KEY_INFO_ACK))
+			return -1;
+		ret = nan_sec_rx_m1(nan, peer, msg, key);
+		break;
+	case NAN_SUBTYPE_DATA_PATH_RESPONSE:
+		if (!(info & WPA_KEY_INFO_MIC))
+			return -1;
+		ret = nan_sec_rx_m2(nan, peer, msg, key);
+		break;
+	case NAN_SUBTYPE_DATA_PATH_CONFIRM:
+		if (!(info & WPA_KEY_INFO_MIC) ||
+		    !(info & WPA_KEY_INFO_ACK) ||
+		    !(info & WPA_KEY_INFO_SECURE))
+			return -1;
+		ret = nan_sec_rx_m3(nan, peer, msg, key);
+		break;
+	case NAN_SUBTYPE_DATA_PATH_KEY_INSTALL:
+		if (!(info & WPA_KEY_INFO_MIC) ||
+		    !(info & WPA_KEY_INFO_SECURE))
+			return -1;
+		ret = nan_sec_rx_m4(nan, peer, msg, key);
+		break;
+	default:
+		wpa_printf(MSG_DEBUG, "NAN: SEC: Invalid frame OUI subtype");
+		return -1;
+	}
+
+	if (ret)
+		return ret;
+
+	instance_id = shared_key_desc->publish_id;
+	if (ndp_sec->i_instance_id && instance_id &&
+	    ndp_sec->i_instance_id != instance_id) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: SEC: Mismatch instance ID in shared key descriptor");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 29bdc866f2..9e5a2274f2 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -342,6 +342,7 @@ OBJS += ../src/nan/nan_util.o
 OBJS += ../src/nan/nan_ndp.o
 OBJS += ../src/nan/nan_ndl.o
 OBJS += ../src/nan/nan_crypto.o
+OBJS += ../src/nan/nan_sec.o
 endif
 
 ifdef CONFIG_OWE
-- 
2.49.0




More information about the Hostap mailing list