[PATCH 30/92] NAN: Add functions for deriving NPK and KEK from KDK

Andrei Otcheretianski andrei.otcheretianski at intel.com
Wed Apr 22 05:23:21 PDT 2026


From: Avraham Stern <avraham.stern at intel.com>

WiFi Aware specification version 4.0 section 7.4.6.2 defines the
derivation of the NPK for opportunistic pairing and KEK from the
KDK in a similar way, with a different label and possibly a different
length. Add functions for deriving the NPK and the KEK from the KDK.
Use a common function for both cases with the suitable parameters for
each case.

Signed-off-by: Avraham Stern <avraham.stern at intel.com>
---
 src/nan/nan_crypto.c | 156 +++++++++++++++++++++++++++++++++++++++++++
 src/nan/nan_i.h      |  11 +++
 2 files changed, 167 insertions(+)

diff --git a/src/nan/nan_crypto.c b/src/nan/nan_crypto.c
index 421e271010..f582830e8d 100644
--- a/src/nan/nan_crypto.c
+++ b/src/nan/nan_crypto.c
@@ -42,8 +42,10 @@ static size_t nan_crypto_cipher_kek_len(enum nan_cipher_suite_id cipher)
 {
 	switch (cipher) {
 	case NAN_CS_SK_CCM_128:
+	case NAN_CS_PK_PASN_128:
 		return 16;
 	case NAN_CS_SK_GCM_256:
+	case NAN_CS_PK_PASN_256:
 		return 32;
 	default:
 		return 0;
@@ -367,3 +369,157 @@ struct wpabuf *nan_crypto_derive_nira_tag(const u8 *nik, size_t nik_len,
 	forced_memzero(tag, sizeof(tag));
 	return tag_buf;
 }
+
+
+/*
+ * nan_crypto_derive_from_kdk - Derive a key from KDK using KDF-HASH-NNN
+ *
+ * @kdk: Key Derivation Key
+ * @kdk_len: Length of KDK in bytes
+ * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256)
+ * @label: Label string for the key derivation
+ * @initiator_nmi: Pairing Initiator NMI address (6 bytes)
+ * @responder_nmi: Pairing Responder NMI address (6 bytes)
+ * @key: Buffer for the derived key
+ * @key_len: number of bytes to derive
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generic function to derive a key from KDK using:
+ * KEY = KDF-HASH-NNN(KDK, label, Initiator NMI || Responder NMI)
+ */
+static int nan_crypto_derive_from_kdk(const u8 *kdk, size_t kdk_len,
+				      enum nan_cipher_suite_id cipher,
+				      const char *label,
+				      const u8 *initiator_nmi,
+				      const u8 *responder_nmi, u8 *key,
+				      size_t key_len)
+{
+	u8 data[ETH_ALEN * 2];
+	int ret = 0;
+
+	if (!kdk || !label || !initiator_nmi || !responder_nmi || !key ||
+	    !key_len) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Invalid parameters for NPK/KEK derivation");
+		return -1;
+	}
+
+	/* Concatenate: Pairing Initiator NMI || Pairing Responder NMI */
+	os_memcpy(data, initiator_nmi, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, responder_nmi, ETH_ALEN);
+
+	if (cipher == NAN_CS_PK_PASN_128) {
+		ret = sha256_prf(kdk, kdk_len, label, data, sizeof(data), key,
+				 key_len);
+	} else if (cipher == NAN_CS_PK_PASN_256) {
+		ret = sha384_prf(kdk, kdk_len, label, data, sizeof(data), key,
+				 key_len);
+	} else {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Unsupported cipher suite for key derivation: %d",
+			   cipher);
+		return -1;
+	}
+
+	if (ret) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NPK/KEK derivation failed (ret=%d)", ret);
+		return ret;
+	}
+
+	wpa_hexdump_key(MSG_DEBUG, "NAN: KDK", kdk, kdk_len);
+	wpa_printf(MSG_DEBUG, "NAN: Label: %s", label);
+	wpa_hexdump_key(MSG_DEBUG, "NAN: Initiator NMI", initiator_nmi,
+			ETH_ALEN);
+	wpa_hexdump_key(MSG_DEBUG, "NAN: Responder NMI", responder_nmi,
+			ETH_ALEN);
+	wpa_hexdump_key(MSG_DEBUG, "NAN: Derived key", key, key_len);
+
+	return 0;
+}
+
+
+/*
+ * nan_crypto_derive_npk - Derive NPK from NM-KDK for opportunistic pairing
+ *
+ * @kdk: NM-KDK (NAN Master Key Derivation Key)
+ * @kdk_len: Length of KDK in bytes
+ * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256)
+ * @initiator_nmi: Pairing Initiator NMI address (6 bytes)
+ * @responder_nmi: Pairing Responder NMI address (6 bytes)
+ * @buf: Buffer for the derived NPK
+ * @buf_len: Length of the buffer  (must be 32 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * NPK = KDF-HASH-256(NM-KDK, "NAN Opportunistic NPK Derivation",
+ *                    Pairing Initiator NMI || Pairing Responder NMI)
+ *
+ * Note: It is unclear whether KDF-HASH-256 means that SHA-256 must be used as
+ * the hash algorithm, or the hash algorithm is determined by the cipher suite.
+ * Usually, NCS-PK-PASN-128 cipher comes with SHA-256 and NCS-PK-PASN-256 with
+ * SHA-384 as defined in Wi-Fi Aware Specification v4.0, section 7.1.2. But for
+ * opportunistic pairing, section 7.6.4.3 specifies KDF-HASH-256 only for NPK
+ * derivation. Does this mean that SHA-256 must be used? In IEEE 802.11-2024,
+ * section 12.13.8, where KDF-HASH-NNN is defined, NNN is the number of bits to
+ * derive, not the hash function. Therefore, we follow the latter
+ * interpretation and use the hash function corresponding to the cipher suite.
+ */
+int nan_crypto_derive_npk(const u8 *kdk, size_t kdk_len,
+			  enum nan_cipher_suite_id cipher,
+			  const u8 *initiator_nmi, const u8 *responder_nmi,
+			  u8 *buf, size_t buf_len)
+{
+	const char *label = "NAN Opportunistic NPK Derivation";
+
+	wpa_printf(MSG_DEBUG, "NAN: Deriving NPK from NM-KDK");
+
+	if (buf_len < NAN_NPK_LEN) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: NPK buffer too small: %zu bytes",
+			   buf_len);
+		return -1;
+	}
+
+	return nan_crypto_derive_from_kdk(kdk, kdk_len, cipher, label,
+					  initiator_nmi, responder_nmi,
+					  buf, buf_len);
+}
+
+
+/*
+ * nan_crypto_derive_kek - Derive KEK from NM-KDK
+ *
+ * @kdk: NM-KDK (NAN Master Key Derivation Key)
+ * @kdk_len: Length of KDK in bytes
+ * @cipher: Cipher suite identifier (NAN_CS_PK_PASN_128 or NAN_CS_PK_PASN_256)
+ * @initiator_nmi: Pairing Initiator NMI address (6 bytes)
+ * @responder_nmi: Pairing Responder NMI address (6 bytes)
+ * @ptk: Buffer for the derived KEK
+ * Returns: 0 on success, -1 on failure
+ *
+ * KEK = KDF-HASH-NNN(NM-KDK, "NAN Management KEK Derivation",
+ *                    Pairing Initiator NMI || Pairing Responder NMI)
+ */
+int nan_crypto_derive_kek(const u8 *kdk, size_t kdk_len,
+			  enum nan_cipher_suite_id cipher,
+			  const u8 *initiator_nmi, const u8 *responder_nmi,
+			  struct wpa_ptk *ptk)
+{
+	const char *label = "NAN Management KEK Derivation";
+
+	wpa_printf(MSG_DEBUG, "NAN: Deriving KEK from NM-KDK");
+
+	if (cipher != NAN_CS_PK_PASN_128 &&
+	    cipher != NAN_CS_PK_PASN_256) {
+		wpa_printf(MSG_DEBUG,
+			   "NAN: Unsupported cipher suite for KEK derivation: %d",
+			   cipher);
+		return -1;
+	}
+
+	ptk->kek_len = nan_crypto_cipher_kek_len(cipher);
+
+	return nan_crypto_derive_from_kdk(kdk, kdk_len, cipher, label,
+					  initiator_nmi, responder_nmi,
+					  ptk->kek, ptk->kek_len);
+}
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index e48f4136da..07fa7a0e85 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -23,6 +23,7 @@ struct nan_config;
 #define NAN_KCK_MAX_LEN 24
 #define NAN_KEK_MAX_LEN 32
 #define NAN_TK_MAX_LEN  32
+#define NAN_NPK_LEN  32
 
 #define NAN_IE_MAX_SIZE 1024
 
@@ -718,6 +719,16 @@ 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);
+int nan_crypto_derive_npk(const u8 *kdk, size_t kdk_len,
+			  enum nan_cipher_suite_id cipher,
+			  const u8 *initiator_nmi, const u8 *responder_nmi,
+			  u8 *buf, size_t buf_len);
+int nan_crypto_derive_kek(const u8 *kdk, size_t kdk_len,
+			  enum nan_cipher_suite_id cipher,
+			  const u8 *initiator_nmi, const u8 *responder_nmi,
+			  struct wpa_ptk *ptk);
+
+
 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);
-- 
2.53.0




More information about the Hostap mailing list