PASN: Add functions to compute PTK, MIC and hash

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 at build
time.

Signed-off-by: Ilan Peer <ilan.peer@intel.com>
This commit is contained in:
Ilan Peer 2020-12-16 13:00:21 +02:00 committed by Jouni Malinen
parent d87f4aea11
commit c6d1a33bb0
6 changed files with 302 additions and 0 deletions

View file

@ -49,6 +49,8 @@
#define WPA_KEY_MGMT_OWE BIT(22)
#define WPA_KEY_MGMT_DPP BIT(23)
#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
#define WPA_KEY_MGMT_PASN BIT(25)
#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
WPA_KEY_MGMT_FT_IEEE8021X | \

View file

@ -1168,6 +1168,266 @@ 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 IEEE P802.11az/D2.7, 12.12.7, the hash algorithm to use is the
* hash algorithm defined for the Base AKM (see Table 9-151 (AKM suite
* selectors)). When there is no Base AKM, the hash algorithm is selected based
* on the pairwise cipher suite provided in the RSNE 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 bool pasn_use_sha384(int akmp, int cipher)
{
return (akmp == WPA_KEY_MGMT_PASN && (cipher == WPA_CIPHER_CCMP_256 ||
cipher == WPA_CIPHER_GCMP_256)) ||
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 (DHss) derived from the PASN ephemeral key
* exchange encoded as an octet string
* @dhss_len: The length of dhss in octets
* @ptk: Buffer for pairwise transient key
* @akmp: Negotiated AKM
* @cipher: Negotiated pairwise cipher
* @kdk_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 kdk_len)
{
u8 tmp[WPA_KCK_MAX_LEN + WPA_TK_MAX_LEN + WPA_KDK_MAX_LEN];
u8 *data;
size_t data_len, ptk_len;
int ret = -1;
const char *label = "PASN PTK Derivation";
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 = KDF(PMK, PASN PTK Derivation, SPA || BSSID || DHss)
*
* KCK = L(PASN-PTK, 0, 256)
* TK = L(PASN-PTK, 256, TK_bits)
* KDK = L(PASN-PTK, 256 + TK_bits, kdk_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->kdk_len = kdk_len;
ptk->kek_len = 0;
ptk->kek2_len = 0;
ptk->kck2_len = 0;
if (ptk->tk_len == 0) {
wpa_printf(MSG_ERROR,
"PASN: Unsupported cipher (0x%x) used in PTK derivation",
cipher);
goto err;
}
ptk_len = ptk->kck_len + ptk->tk_len + ptk->kdk_len;
if (ptk_len > sizeof(tmp))
goto err;
if (pasn_use_sha384(akmp, cipher)) {
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA384");
if (sha384_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
goto err;
} else {
wpa_printf(MSG_DEBUG, "PASN: PTK derivation using SHA256");
if (sha256_prf(pmk, pmk_len, label, 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 (kdk_len) {
os_memcpy(ptk->kdk, tmp + WPA_PASN_KCK_LEN + ptk->tk_len,
ptk->kdk_len);
wpa_hexdump_key(MSG_DEBUG, "PASN: KDK:",
ptk->kdk, ptk->kdk_len);
}
forced_memzero(tmp, 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 frame RSNE. For calculating the MIC for the 3rd PASN frame, this
* should hold the hash of the 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);
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 a 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)

View file

@ -219,6 +219,8 @@ struct wpa_eapol_key {
#define WPA_KDK_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
@ -617,4 +619,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 kdk_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 */

View file

@ -371,6 +371,14 @@ L_CFLAGS += -DCONFIG_WIFI_DISPLAY
OBJS += wifi_display.c
endif
ifdef CONFIG_PASN
L_CFLAGS += -DCONFIG_PASN
NEED_HMAC_SHA256_KDF=y
NEED_HMAC_SHA384_KDF=y
NEED_SHA256=y
NEED_SHA384=y
endif
ifdef CONFIG_HS20
OBJS += hs20_supplicant.c
L_CFLAGS += -DCONFIG_HS20

View file

@ -390,6 +390,14 @@ CFLAGS += -DCONFIG_WIFI_DISPLAY
OBJS += wifi_display.o
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_HS20
OBJS += hs20_supplicant.o
CFLAGS += -DCONFIG_HS20

View file

@ -620,3 +620,9 @@ CONFIG_DPP=y
# support for this by default, but that functionality is subject to be removed
# in the future.
#CONFIG_NO_TKIP=y
# Pre-Association Security Negotiation (PASN)
# Experimental implementation based on IEEE P802.11z/D2.6 and the protocol
# design is still subject to change. As such, this should not yet be enabled in
# production use.
#CONFIG_PASN=y