[02/14] PASN: Add functions to compute PTK, MIC and hash
diff mbox series

Message ID 20200224091529.15259-3-ilan.peer@intel.com
State Changes Requested
Headers show
Series
  • Support base Pre association Security Negotiation (PASN)
Related show

Commit Message

Peer, Ilan Feb. 24, 2020, 9:15 a.m. UTC
1. Add a function to derive the PTK from a PMK and additional data.
2. Add a function to calculate the MIC for a PASN frames.
3. Add a function to compute the hash of an authentication frame body.

The above are built only in case that CONFIG_PASN is enabled in build
time.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
---
 src/common/defs.h        |   6 +
 src/common/wpa_common.c  | 263 +++++++++++++++++++++++++++++++++++++++
 src/common/wpa_common.h  |  18 +++
 wpa_supplicant/Makefile  |   8 ++
 wpa_supplicant/defconfig |   3 +
 5 files changed, 298 insertions(+)

Comments

Jouni Malinen Feb. 29, 2020, 10:17 p.m. UTC | #1
On Mon, Feb 24, 2020 at 11:15:17AM +0200, Ilan Peer wrote:
> 1. Add a function to derive the PTK from a PMK and additional data.
> 2. Add a function to calculate the MIC for a PASN frames.
> 3. Add a function to compute the hash of an authentication frame body.
> 
> The above are built only in case that CONFIG_PASN is enabled in build
> time.

> diff --git a/src/common/defs.h b/src/common/defs.h
> @@ -59,6 +59,12 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
> +#ifdef CONFIG_PASN
> +/* TODO: PASN AKM is not yet defined in the spec. */
> +#define WPA_KEY_MGMT_PASN BIT(25)
> +#endif /* CONFIG_PASN */

This WPA_KEY_MGMT_* bitmap is implementation specific and that itself
would not be much of an issue to extend, but the PASN AKM suite selector
itself is waiting for ANA assignment and as such, does not look ready to
be applied yet. Similarly, I'm not yet convinced that the crypto design
is yet finalized, so this patch as whole looks a bit too early at this
point in time.

Patch
diff mbox series

diff --git a/src/common/defs.h b/src/common/defs.h
index 1e21ec2de4..736ed7ff09 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -59,6 +59,12 @@  typedef enum { FALSE = 0, TRUE = 1 } Boolean;
 #define WPA_KEY_MGMT_DPP BIT(23)
 #define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
 
+#ifdef CONFIG_PASN
+/* TODO: PASN AKM is not yet defined in the spec. */
+#define WPA_KEY_MGMT_PASN BIT(25)
+#endif /* CONFIG_PASN */
+
+
 #define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
 			 WPA_KEY_MGMT_FT_IEEE8021X | \
 			 WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index deae2a35f9..1a67f44720 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -1157,6 +1157,269 @@  int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
 #endif /* CONFIG_IEEE80211R */
 
 
+#ifdef CONFIG_PASN
+
+/*
+ * pasn_use_sha384 - Should SHA384 be used or SHA256
+ *
+ * @akmp: Authentication and Key management protocol
+ * @cipher: the cipher suite
+ *
+ * According to Draft P802.11az_D2.0, section 12.13.7, the hash algorithm to use
+ * is the hash algorithm defined for the Base AKM (see Table 9-144 (AKM suite
+ * selectors)). When there is no Base AKM, the hash algorithm is selected based
+ * on the pairwise Cipher Suite provided in the RSNE provided by the AP in the
+ * second PASN frame. SHA-256 is used as the hash algorithm, except for the
+ * ciphers 00-0F-AC:9 and 00-0F-AC:10 for which SHA-384 is used.
+ */
+static u8 pasn_use_sha384(int akmp, int cipher)
+{
+	return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
+					      cipher == WPA_CIPHER_GCMP)) ||
+		wpa_key_mgmt_sha384(akmp);
+}
+
+
+/**
+ * pasn_pmk_to_ptk - Calculate PASN PTK from PMK, addresses, etc.
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of PMK
+ * @spa: Suppplicant address
+ * @bssid: AP bssid
+ * @dhss: is the shared secret derived from the PASN ephemeral key exchange
+ *     encoded as an octet string
+ * @dhss_len: the length of dhss
+ * @ptk: Buffer for pairwise transient key
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * @hltk_len: the length in octets that should be derived for HTLK. Can be zero.
+ * Returns: 0 on success, -1 on failure
+ */
+int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
+		    const u8 *spa, const u8 *bssid,
+		    const u8 *dhss, size_t dhss_len,
+		    struct wpa_ptk *ptk, int akmp, int cipher,
+		    size_t hltk_len)
+{
+	u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_HLTK_MAX_LEN];
+	u8 *data;
+	size_t data_len, ptk_len;
+	int ret = -1;
+
+	if (!pmk || !pmk_len) {
+		wpa_printf(MSG_ERROR, "PASN: No PMK set for PTK derivation");
+		return -1;
+	}
+
+	if (!dhss || !dhss_len) {
+		wpa_printf(MSG_ERROR, "PASN: No DHSS set for PTK derivation");
+		return -1;
+	}
+
+	/*
+	 * PASN-PTK= PRF-X(PMK, “PASN PTK Derivation”, SPA || BSSID || DHss)
+	 *
+	 * KCK = L(PASN-PTK, 0, 256)
+	 * TK = L(PASN-PTK, 256, Tk_bits)
+	 * HLTK = L(PASN-PTK, 256 + Tk_bits, hltk_len * 8)
+	 */
+	data_len = 2 * ETH_ALEN + dhss_len;
+	data = os_zalloc(data_len);
+	if (!data)
+		return -1;
+
+	os_memcpy(data, spa, ETH_ALEN);
+	os_memcpy(data + ETH_ALEN, bssid, ETH_ALEN);
+	os_memcpy(data + 2 * ETH_ALEN, dhss, dhss_len);
+
+	ptk->kck_len = WPA_PASN_KCK_LEN;
+	ptk->tk_len = wpa_cipher_key_len(cipher);
+	ptk->hltk_len = hltk_len;
+	ptk->kek_len = 0;
+	ptk->kek2_len = 0;
+	ptk->kck2_len = 0;
+
+	if (ptk->tk_len == 0) {
+		wpa_printf(MSG_ERROR,
+			   "WPA: Unsupported cipher (0x%x) used in PTK derivation",
+			   cipher);
+		goto err;
+	}
+
+	ptk_len = ptk->kck_len + ptk->tk_len + ptk->hltk_len;
+
+	if (pasn_use_sha384(akmp, cipher)) {
+		wpa_printf(MSG_DEBUG, "PASN: PTK derivation using PRF(SHA384)");
+
+		if (sha384_prf(pmk, pmk_len, "PASN PTK Derivation",
+			       data, data_len, tmp, ptk_len) < 0)
+			goto err;
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: PTK derivation using PRF(SHA256)");
+
+		if (sha256_prf(pmk, pmk_len, "PASN PTK Derivation",
+			       data, data_len, tmp, ptk_len) < 0)
+			goto err;
+	}
+
+	wpa_printf(MSG_DEBUG,
+		   "PASN: PTK derivation:  SPA=" MACSTR " BSSID=" MACSTR,
+		   MAC2STR(spa), MAC2STR(bssid));
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: DHss", dhss, dhss_len);
+	wpa_hexdump_key(MSG_DEBUG, "PASN: PMK", pmk, pmk_len);
+	wpa_hexdump_key(MSG_DEBUG, "PASN: PASN-PTK", tmp, ptk_len);
+
+	os_memcpy(ptk->kck, tmp, WPA_PASN_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "PASN: KCK:", ptk->kck, WPA_PASN_KCK_LEN);
+
+	os_memcpy(ptk->tk, tmp + WPA_PASN_KCK_LEN, ptk->tk_len);
+	wpa_hexdump_key(MSG_DEBUG, "PASN: TK:", ptk->tk, ptk->tk_len);
+
+	if (hltk_len) {
+		os_memcpy(ptk->hltk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len,
+			  ptk->hltk_len);
+		wpa_hexdump_key(MSG_DEBUG, "PASN: HLTK:", ptk->hltk,
+				ptk->hltk_len);
+	}
+
+	os_memset(tmp, 0, sizeof(tmp));
+	ret = 0;
+err:
+	bin_clear_free(data, data_len);
+	return ret;
+}
+
+
+/*
+ * pasn_mic_len - Returns the MIC length for PASN authentication
+ */
+u8 pasn_mic_len(int akmp, int cipher)
+{
+	if (pasn_use_sha384(akmp, cipher))
+		return 24;
+
+	return 16;
+}
+
+
+/**
+ * pasn_mic - Calculate PASN MIC
+ * @kck: The key confirmation key for the PASN PTKSA
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * @addr1: for the 2nd PASN frame supplicant address; for the 3rd frame the
+ *     BSSID
+ * @addr2: for the 2nd PASN frame the BSSID; for the 3rd frame the supplicant
+ *     address
+ * @data: For calculating the MIC for the 2nd PASN frame, this should hold the
+ *    beacon RSN IE. For the calculating the MIC for the 3rd PASN frame, this
+ *    should hold the HASH of body of the PASN 1st frame.
+ * @data_len: the length of data
+ * @frame: The body of the PASN frame including the MIC element with the Octets
+ *     in the MIC field of the MIC element set to 0.
+ * @frame_len: the length of frame
+ * @mic: buffer to hold the MIC on success. Should be big enough to handle the
+ *     maximal MIC length
+ * Returns: 0 on success, -1 on failure
+ */
+int pasn_mic(const u8 *kck, int akmp, int cipher,
+	     const u8 *addr1, const u8 *addr2,
+	     const u8 *data, size_t data_len,
+	     const u8 *frame, size_t frame_len, u8 *mic)
+{
+	u8 *buf;
+	u8 hash[SHA384_MAC_LEN];
+	size_t buf_len = 2 * ETH_ALEN + data_len + frame_len;
+	int ret = -1;
+
+	if (!kck) {
+		wpa_printf(MSG_ERROR, "PASN: No KCK for MIC calculation");
+		return -1;
+	}
+
+	if (!data || !data_len) {
+		wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation");
+		return -1;
+	}
+
+	if (!frame || !frame_len) {
+		wpa_printf(MSG_ERROR, "PASN: invalid data for MIC calculation");
+		return -1;
+	}
+
+	buf = os_zalloc(buf_len);
+	if (!buf)
+		return -1;
+
+	os_memcpy(buf, addr1, ETH_ALEN);
+	os_memcpy(buf + ETH_ALEN, addr2, ETH_ALEN);
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: data: ", data, data_len);
+	os_memcpy(buf + 2 * ETH_ALEN, data, data_len);
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: frame: ", frame, frame_len);
+	os_memcpy(buf + 2 * ETH_ALEN + data_len, frame, frame_len);
+
+	os_memset(hash, 0, sizeof(hash));
+
+	wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: KCK: ", kck, WPA_PASN_KCK_LEN);
+	wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: buf: ", buf, buf_len);
+
+	if (pasn_use_sha384(akmp, cipher)) {
+		wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA384");
+
+		if (hmac_sha384(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
+			goto err;
+
+		os_memcpy(mic, hash, 24);
+		wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 24);
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: MIC using HMAC-SHA256");
+
+		if (hmac_sha256(kck, WPA_PASN_KCK_LEN, buf, buf_len, hash))
+			goto err;
+
+		os_memcpy(mic, hash, 16);
+		wpa_hexdump_key(MSG_DEBUG, "PASN: MIC: mic: ", mic, 16);
+	}
+
+	ret = 0;
+err:
+	bin_clear_free(buf, buf_len);
+	return ret;
+}
+
+
+/*
+ * pasn_auth_frame_hash - Computes an hash of an authentication frame body
+ *
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * @data: pointer to the authentication frame body
+ * @len: length of the authentication frame body
+ * @hash: on return would hold the computed hash. Should be big enough to handle
+ *     SHA384.
+ * Returns: 0 on success, -1 on failure
+ */
+int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
+			 u8 *hash)
+{
+	if (pasn_use_sha384(akmp, cipher)) {
+		wpa_printf(MSG_DEBUG, "PASN: frame hash using SHA-384");
+
+		return sha384_vector(1, &data, &len, hash);
+
+	} else {
+		wpa_printf(MSG_DEBUG, "PASN: frame hash using SHA-256");
+
+		return sha256_vector(1, &data, &len, hash);
+	}
+}
+
+#endif /* CONFIG_PASN */
+
+
 static int rsn_selector_to_bitfield(const u8 *s)
 {
 	if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 3e9e00558e..3ecb85e8c2 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -208,6 +208,8 @@  struct wpa_eapol_key {
 #define WPA_HLTK_MAX_LEN 32
 #define FILS_ICK_MAX_LEN 48
 #define FILS_FT_MAX_LEN 48
+#define WPA_PASN_KCK_LEN 32
+#define WPA_PASN_MIC_MAX_LEN 24
 
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
@@ -591,4 +593,20 @@  int wpa_use_cmac(int akmp);
 int wpa_use_aes_key_wrap(int akmp);
 int fils_domain_name_hash(const char *domain, u8 *hash);
 
+int pasn_pmk_to_ptk(const u8 *pmk, size_t pmk_len,
+		    const u8 *spa, const u8 *bssid,
+		    const u8 *dhss, size_t dhss_len,
+		    struct wpa_ptk *ptk, int akmp, int cipher,
+		    size_t hltk_len);
+
+u8 pasn_mic_len(int akmp, int cipher);
+
+int pasn_mic(const u8 *kck, int akmp, int cipher,
+	     const u8 *addr1, const u8 *addr2,
+	     const u8 *data, size_t data_len,
+	     const u8 *frame, size_t frame_len, u8 *mic);
+
+int pasn_auth_frame_hash(int akmp, int cipher, const u8 *data, size_t len,
+			 u8 *hash);
+
 #endif /* WPA_COMMON_H */
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
index e41464f4d6..d40804d528 100644
--- a/wpa_supplicant/Makefile
+++ b/wpa_supplicant/Makefile
@@ -386,6 +386,14 @@  CFLAGS += -DCONFIG_P2P_STRICT
 endif
 endif
 
+ifdef CONFIG_PASN
+CFLAGS += -DCONFIG_PASN
+NEED_HMAC_SHA256_KDF=y
+NEED_HMAC_SHA384_KDF=y
+NEED_SHA256=y
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_WIFI_DISPLAY
 CFLAGS += -DCONFIG_WIFI_DISPLAY
 OBJS += wifi_display.o
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
index 5308b494b7..847ebebcb2 100644
--- a/wpa_supplicant/defconfig
+++ b/wpa_supplicant/defconfig
@@ -603,3 +603,6 @@  CONFIG_BGSCAN_SIMPLE=y
 
 # Device Provisioning Protocol (DPP)
 CONFIG_DPP=y
+
+# Pre Association Security Negotiation (PASN)
+#CONFIG_PASN=y