[RFC 36/56] NAN: Add NAN cryptographic functions
Andrei Otcheretianski
andrei.otcheretianski at intel.com
Sun Dec 7 03:18:45 PST 2025
From: Ilan Peer <ilan.peer at intel.com>
1. PTK derivation from PMK.
2. Authentication token calculation.
3. EAPOL key MIC calculation
4. PMKID calculation.
Add few definitions that are specific to NAN to nan_defs.h.
Signed-off-by: Ilan Peer <ilan.peer at intel.com>
---
src/common/nan_defs.h | 4 +
src/nan/Makefile | 3 +-
src/nan/nan_crypto.c | 398 ++++++++++++++++++++++++++++++++++++++++
src/nan/nan_i.h | 36 ++++
wpa_supplicant/Makefile | 1 +
5 files changed, 440 insertions(+), 2 deletions(-)
create mode 100644 src/nan/nan_crypto.c
diff --git a/src/common/nan_defs.h b/src/common/nan_defs.h
index 3e10a3358d..208a8c1784 100644
--- a/src/common/nan_defs.h
+++ b/src/common/nan_defs.h
@@ -453,6 +453,10 @@ struct ieee80211_nan_qos {
#define NAN_QOS_MIN_SLOTS_NO_PREF 0
#define NAN_QOS_MAX_LATENCY_NO_PREF 0xffff
+#define NAN_AUTH_TOKEN_LEN 16
+#define NAN_KEY_MIC_LEN 16
+#define NAN_KEY_MIC_24_LEN 24
+
/* See Table 121 (Cipher Suite attribute field format) */
enum nan_cipher_suite_id {
NAN_CS_NONE = 0,
diff --git a/src/nan/Makefile b/src/nan/Makefile
index 38665ea234..7d79a2d77e 100644
--- a/src/nan/Makefile
+++ b/src/nan/Makefile
@@ -1,3 +1,2 @@
-LIB_OBJS= nan.o nan_util.o nan_ndp.o nan_ndl.o
-
+LIB_OBJS= nan.o nan_util.o nan_ndp.o nan_ndl.o nan_crypto.o
include ../lib.rules
diff --git a/src/nan/nan_crypto.c b/src/nan/nan_crypto.c
new file mode 100644
index 0000000000..9dceb2f648
--- /dev/null
+++ b/src/nan/nan_crypto.c
@@ -0,0 +1,398 @@
+/*
+ * Wi-Fi Aware - NAN Data link cryptography functions
+ * 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 "crypto/sha256.h"
+#include "crypto/sha384.h"
+#include "crypto/crypto.h"
+#include "nan_i.h"
+
+#define NAN_KCK_MAX_LEN 24
+#define NAN_KEK_MAX_LEN 32
+#define NAN_TK_MAX_LEN 32
+
+#define NAN_PTK_LABEL "NAN Pairwise key expansion"
+#define NAN_PMKID_LABEL "NAN PMK Name"
+
+/* NAN ciphers use only SHA256 and SHA384, and SHA384 has a bigger digest */
+#define MAX_MAC_LEN SHA384_MAC_LEN
+
+static u32 nan_crypto_cipher_kck_len(enum nan_cipher_suite_id cipher)
+{
+ switch (cipher) {
+ case NAN_CS_SK_CCM_128:
+ return 16;
+ case NAN_CS_SK_GCM_256:
+ return 24;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 nan_crypto_cipher_kek_len(enum nan_cipher_suite_id cipher)
+{
+ switch (cipher) {
+ case NAN_CS_SK_CCM_128:
+ return 16;
+ case NAN_CS_SK_GCM_256:
+ return 32;
+ default:
+ return 0;
+ }
+}
+
+
+static u32 nan_cipher_key_len(enum nan_cipher_suite_id cipher)
+{
+ switch (cipher) {
+ case NAN_CS_SK_CCM_128:
+ return 16;
+ case NAN_CS_SK_GCM_256:
+ return 32;
+ default:
+ return 0;
+ }
+}
+
+
+static int nan_crypto_sha256(const u8 *plaintext, u32 psize, u8 *output)
+{
+ const u8 *addrs[1];
+ size_t lens[1];
+
+ addrs[0] = plaintext;
+ lens[0] = psize;
+
+ return sha256_vector(1, addrs, lens, output);
+}
+
+
+static int nan_crypto_sha384(const u8 *plaintext, u32 psize, u8 *output)
+{
+ const u8 *addrs[1];
+ size_t lens[1];
+
+ addrs[0] = plaintext;
+ lens[0] = psize;
+
+ return sha384_vector(1, addrs, lens, output);
+}
+
+
+static int nan_crypto_hmac_sha256(const u8 *key, u32 ksize,
+ const u8 *plaintext,
+ u32 psize, u8 *output)
+{
+ return hmac_sha256(key, ksize, plaintext, psize, output);
+}
+
+
+static int nan_crypto_hmac_sha384(const u8 *key, u32 ksize,
+ const u8 *plaintext,
+ u32 psize, u8 *output)
+{
+ return hmac_sha384(key, ksize, plaintext, psize, output);
+}
+
+
+/*
+ * nan_crypto_kdf - NAN key derivation function
+ *
+ * @key: PMK
+ * @key_len: PMK length
+ * @label: Unique string used for the key derivation for NAN
+ * @data: Input for the key derivation
+ * @data_len: Length of &input
+ * @buf: Buffer to hold the derived keys
+ * @buf_len: Length of &buf
+ * @hmac_func: Pointer to a hmac function that will be used to compute the
+ * digest for each iteration.
+ * @mac_len: the size of the digest computed by %hmac_func
+ */
+static int nan_crypto_kdf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len, u8 *buf,
+ size_t buf_len,
+ int (*hmac_func)(const u8 *key, u32 ksize,
+ const u8 *plaintext, u32 psize,
+ u8 *output),
+ u32 mac_len)
+{
+ u16 label_len = os_strlen(label);
+ u32 input_len, pos;
+ u8 *input;
+ int res = -1;
+ u16 counter = 1;
+
+ /*
+ * counter length (2) + label length + data length +
+ * number of bits (2)
+ */
+ input_len = 2 + label_len + data_len + 2;
+ input = os_zalloc(input_len);
+ if (!input)
+ return -1;
+
+ os_memcpy(input + 2, label, os_strlen(label));
+ os_memcpy(input + 2 + label_len, data, data_len);
+ WPA_PUT_LE16(input + 2 + label_len + data_len, (buf_len * 8));
+
+ pos = 0;
+ while (pos < buf_len) {
+ u32 plen = buf_len - pos;
+
+ WPA_PUT_LE16(input, counter);
+
+ wpa_hexdump_key(MSG_DEBUG, "NAN: KDF: RAW DATA",
+ input, input_len);
+ if (plen >= mac_len) {
+ if ((*hmac_func)(key, key_len, input, input_len,
+ &buf[pos]) < 0)
+ goto fail;
+ pos += mac_len;
+ } else {
+ u8 hash[MAX_MAC_LEN];
+
+ if ((*hmac_func)(key, key_len, input, input_len,
+ hash) < 0)
+ goto fail;
+ os_memcpy(&buf[pos], hash, plen);
+ pos += plen;
+ forced_memzero(hash, sizeof(hash));
+ break;
+ }
+ counter++;
+ }
+
+ res = 0;
+fail:
+ forced_memzero(input, input_len);
+ os_free(input);
+
+ return res;
+}
+
+
+/*
+ * nan_crypto_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ *
+ * @pmk: Pairwise master key
+ * @iaddr: Initiator address
+ * @raddr: Remote address
+ * @inonce: Initiator nonce
+ * @rnonce: Remote nonce
+ * @ptk: Buffer for Pairwise Transient Key
+ * @cipher: Negotiated pairwise cipher
+ * returns: 0 on success, negative value of failure
+ */
+int nan_crypto_pmk_to_ptk(const u8 *pmk, const u8 *iaddr, const u8 *raddr,
+ const u8 *inonce, const u8 *rnonce,
+ struct nan_ptk *ptk,
+ enum nan_cipher_suite_id cipher)
+{
+ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+ u8 tmp[NAN_KCK_MAX_LEN + NAN_KEK_MAX_LEN + NAN_TK_MAX_LEN];
+ size_t ptk_len;
+ int ret;
+
+ if (cipher != NAN_CS_SK_CCM_128 && cipher != NAN_CS_SK_GCM_256)
+ return -1;
+
+ if (!ptk)
+ return -1;
+
+ os_memcpy(data, iaddr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, raddr, ETH_ALEN);
+ os_memcpy(data + 2 * ETH_ALEN, inonce, WPA_NONCE_LEN);
+ os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, rnonce,
+ WPA_NONCE_LEN);
+
+ ptk->kck_len = nan_crypto_cipher_kck_len(cipher);
+ ptk->kek_len = nan_crypto_cipher_kek_len(cipher);
+ ptk->tk_len = nan_cipher_key_len(cipher);
+ ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+ if (cipher == NAN_CS_SK_CCM_128)
+ ret = nan_crypto_kdf(pmk, PMK_LEN, NAN_PTK_LABEL, data,
+ sizeof(data), tmp, ptk_len,
+ nan_crypto_hmac_sha256,
+ SHA256_MAC_LEN);
+ else
+ ret = nan_crypto_kdf(pmk, PMK_LEN, NAN_PTK_LABEL, data,
+ sizeof(data), tmp, ptk_len,
+ nan_crypto_hmac_sha384,
+ SHA384_MAC_LEN);
+
+ if (ret)
+ goto out;
+
+ wpa_hexdump_key(MSG_DEBUG, "NAN: PMK", pmk, PMK_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: iaddr", iaddr, ETH_ALEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: raddr", raddr, ETH_ALEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: inonce", inonce, WPA_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: rnonce", rnonce, WPA_NONCE_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: PTK", tmp, ptk_len);
+
+ os_memcpy(ptk->kck, tmp, ptk->kck_len);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: KCK", ptk->kck, ptk->kck_len);
+
+ os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: KEK", ptk->kek, ptk->kek_len);
+
+ os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: TK", ptk->tk, ptk->tk_len);
+
+out:
+ forced_memzero(tmp, sizeof(data));
+ forced_memzero(tmp, sizeof(tmp));
+ return ret;
+}
+
+
+/*
+ * nan_crypto_calc_pmkid - Calculate a NAN PMKID
+ * @pmk: Pairwise Master Key
+ * @iaddr: Initiator address
+ * @raddr: Remote address
+ * @serv_id: ID of the service providing the PMK
+ * @cipher: Negotiated pairwise cipher
+ * @pmkid: Buffer to hold the pmkid
+ * returns: 0 on success, negative value of failure
+ */
+int nan_crypto_calc_pmkid(const u8 *pmk, const u8 *iaddr, const u8 *raddr,
+ const u8 *serv_id,
+ enum nan_cipher_suite_id cipher, u8 *pmkid)
+{
+ u8 data[sizeof(NAN_PMKID_LABEL) - 1 + 2 * ETH_ALEN +
+ NAN_SERVICE_ID_LEN];
+ u8 digest[SHA384_MAC_LEN];
+ int ret;
+
+ os_memset(data, 0, sizeof(data));
+ os_memset(digest, 0, sizeof(digest));
+
+ if (cipher != NAN_CS_SK_CCM_128 && cipher != NAN_CS_SK_GCM_256)
+ return -1;
+
+ if (!serv_id)
+ return -1;
+
+ if ((*(u16 *)(serv_id + 0) | *(u16 *)(serv_id + 2) |
+ *(u16 *)(serv_id + 4)) == 0)
+ return -1;
+
+ os_memcpy(data, NAN_PMKID_LABEL, sizeof(NAN_PMKID_LABEL) - 1);
+ os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1, iaddr, ETH_ALEN);
+ os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1 + ETH_ALEN, raddr,
+ ETH_ALEN);
+ os_memcpy(data + sizeof(NAN_PMKID_LABEL) - 1 + 2 * ETH_ALEN, serv_id,
+ NAN_SERVICE_ID_LEN);
+
+ wpa_hexdump_key(MSG_DEBUG, "NAN: PMKID DATA", data, sizeof(data));
+
+ if (cipher == NAN_CS_SK_CCM_128)
+ ret = nan_crypto_hmac_sha256(pmk, PMK_LEN, data,
+ sizeof(data), digest);
+ else
+ ret = nan_crypto_hmac_sha384(pmk, PMK_LEN, data,
+ sizeof(data), digest);
+
+ if (ret)
+ goto out;
+
+ os_memcpy(pmkid, digest, PMKID_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: PMKID", pmkid, PMKID_LEN);
+
+out:
+ forced_memzero(digest, sizeof(digest));
+ return ret;
+}
+
+
+/*
+ * nan_crypto_calc_auth_token - calculate authentication token
+ *
+ * @buf: Buffer on which to calculate the authentication token
+ * @len: Length of &buf
+ * @cipher: Negotiated nan cipher
+ * @token: Buffer to hold the token (NAN_AUTH_TOKEN_LEN octets)
+ * returns 0 on success, and a negative error value on failure.
+ */
+int nan_crypto_calc_auth_token(enum nan_cipher_suite_id cipher,
+ const u8 *buf, size_t len, u8 *token)
+{
+ u8 hash[MAX_MAC_LEN];
+ int ret;
+
+ if (cipher != NAN_CS_SK_CCM_128 && cipher != NAN_CS_SK_GCM_256)
+ return -1;
+
+ if (cipher == NAN_CS_SK_CCM_128)
+ ret = nan_crypto_sha256(buf, len, hash);
+ else
+ ret = nan_crypto_sha384(buf, len, hash);
+
+ if (ret)
+ return ret;
+
+ os_memcpy(token, hash, NAN_AUTH_TOKEN_LEN);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: AUTH_TOKEN_DATA", buf, len);
+ wpa_hexdump_key(MSG_DEBUG, "NAN: AUTH TOKEN", token,
+ NAN_AUTH_TOKEN_LEN);
+
+ forced_memzero(hash, sizeof(hash));
+
+ return ret;
+}
+
+
+/*
+ * nan_crypto_key_mic - Calculate MIC over the given buffer
+ *
+ * @buf: Buffer on which to calculate the MIC
+ * @len: Length of &buf
+ * @kck: Key Confirmation Key
+ * @kck_len: Length of &kck
+ * @cipher: Cipher suite identifier.
+ * @mic: On successful return, would hold the MIC.
+ * return 0 on success, and a negative error value on failure.
+ */
+int nan_crypto_key_mic(const u8 *buf, size_t len, const u8 *kck,
+ size_t kck_len, u8 cipher, u8 *mic)
+{
+ u8 digest[SHA384_MAC_LEN];
+ u8 mic_len;
+ int ret;
+
+ os_memset(digest, 0, sizeof(digest));
+
+ if (cipher != NAN_CS_SK_CCM_128 && cipher != NAN_CS_SK_GCM_256)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "MIC DATA", buf, len);
+ wpa_hexdump_key(MSG_DEBUG, "MIC KEY", kck, kck_len);
+
+ if (cipher == NAN_CS_SK_CCM_128) {
+ mic_len = NAN_KEY_MIC_LEN;
+ ret = nan_crypto_hmac_sha256(kck, kck_len, buf, len, digest);
+ } else {
+ mic_len = NAN_KEY_MIC_24_LEN;
+ ret = nan_crypto_hmac_sha384(kck, kck_len, buf, len, digest);
+ }
+
+ if (ret)
+ return ret;
+
+ os_memcpy(mic, digest, mic_len);
+ forced_memzero(digest, sizeof(digest));
+
+ wpa_hexdump_key(MSG_DEBUG, "MIC", mic, mic_len);
+ return 0;
+}
diff --git a/src/nan/nan_i.h b/src/nan/nan_i.h
index 6733870cf1..23a153dde9 100644
--- a/src/nan/nan_i.h
+++ b/src/nan/nan_i.h
@@ -11,6 +11,7 @@
#include "common/ieee802_11_defs.h"
#include "common/nan_defs.h"
+#include "common/wpa_common.h"
#include "nan.h"
#include "list.h"
@@ -18,6 +19,30 @@ struct nan_config;
#define NAN_INVALID_MAP_ID 0xff
+#define NAN_KCK_MAX_LEN 24
+#define NAN_KEK_MAX_LEN 32
+#define NAN_TK_MAX_LEN 32
+
+/**
+ * struct nan_ptk - NAN Pairwise Transient Key
+ *
+ * @kck: Key Confirmation Key
+ * @kek: Key encryption Key
+ * @tk: Transient Key
+ * @kck_len: Length of &kck
+ * @kek_len: Length of &kek
+ * @tk_len: Length of &tk
+ */
+struct nan_ptk {
+ u8 kck[NAN_KCK_MAX_LEN];
+ u8 kek[NAN_KEK_MAX_LEN];
+ u8 tk[NAN_TK_MAX_LEN];
+
+ size_t kck_len;
+ size_t kek_len;
+ size_t tk_len;
+};
+
/*
* enum nan_ndp_state - State of NDP establishment
* @NAN_NDP_STATE_NONE: No NDP establishment in progress.
@@ -468,4 +493,15 @@ struct bitfield * nan_avail_entries_to_bf(struct nan_data *nan,
void nan_ndp_terminated(struct nan_data *nan, struct nan_peer *peer,
struct nan_ndp_id *ndp_id, const u8 *local_ndi,
const u8 *peer_ndi, enum nan_reason reason);
+int nan_crypto_pmk_to_ptk(const u8 *pmk, const u8 *iaddr, const u8 *raddr,
+ const u8 *inonce, const u8 *rnonce,
+ struct nan_ptk *ptk,
+ enum nan_cipher_suite_id cipher);
+int nan_crypto_calc_pmkid(const u8 *pmk, const u8 *iaddr, const u8 *raddr,
+ const u8 *serv_id,
+ enum nan_cipher_suite_id cipher, u8 *pmkid);
+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);
#endif
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index 662b60cb16..f57bcaf1eb 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -341,6 +341,7 @@ OBJS += ../src/common/nan_de.o
OBJS += ../src/nan/nan_util.o
OBJS += ../src/nan/nan_ndp.o
OBJS += ../src/nan/nan_ndl.o
+OBJS += ../src/nan/nan_crypto.o
endif
ifdef CONFIG_OWE
--
2.49.0
More information about the Hostap
mailing list