FILS: Add DHss into FILS-Key-Data derivation when using FILS SK+PFS
This part is missing from IEEE Std 802.11ai-2016, but the lack of DHss here means there would not be proper PFS for the case where PMKSA caching is used with FILS SK+PFS authentication. This was not really the intent of the FILS design and that issue was fixed during REVmd work with the changes proposed in https://mentor.ieee.org/802.11/dcn/17/11-17-0906-04-000m-fils-fixes.docx that add DHss into FILS-Key-Data (and PTK, in practice) derivation for the PMKSA caching case so that a unique ICK, KEK, and TK are derived even when using the same PMK. Note: This is not backwards compatible, i.e., this breaks PMKSA caching with FILS SK+PFS if only STA or AP side implementation is updated. Signed-off-by: Jouni Malinen <jouni@qca.qualcomm.com>
This commit is contained in:
parent
41b8191485
commit
4cada9dcc1
7 changed files with 62 additions and 18 deletions
|
@ -1417,6 +1417,11 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
|
||||||
}
|
}
|
||||||
pmk = pmk_buf;
|
pmk = pmk_buf;
|
||||||
|
|
||||||
|
/* Don't use DHss in PTK derivation if PMKSA caching is not
|
||||||
|
* used. */
|
||||||
|
wpabuf_clear_free(sta->fils_dh_ss);
|
||||||
|
sta->fils_dh_ss = NULL;
|
||||||
|
|
||||||
if (sta->fils_erp_pmkid_set) {
|
if (sta->fils_erp_pmkid_set) {
|
||||||
/* TODO: get PMKLifetime from WPA parameters */
|
/* TODO: get PMKLifetime from WPA parameters */
|
||||||
unsigned int dot11RSNAConfigPMKLifetime = 43200;
|
unsigned int dot11RSNAConfigPMKLifetime = 43200;
|
||||||
|
@ -1449,6 +1454,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
|
||||||
|
|
||||||
if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
|
if (fils_auth_pmk_to_ptk(sta->wpa_sm, pmk, pmk_len,
|
||||||
sta->fils_snonce, fils_nonce,
|
sta->fils_snonce, fils_nonce,
|
||||||
|
sta->fils_dh_ss ?
|
||||||
|
wpabuf_head(sta->fils_dh_ss) : NULL,
|
||||||
|
sta->fils_dh_ss ?
|
||||||
|
wpabuf_len(sta->fils_dh_ss) : 0,
|
||||||
sta->fils_g_sta, pub) < 0) {
|
sta->fils_g_sta, pub) < 0) {
|
||||||
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
*resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||||
wpabuf_free(data);
|
wpabuf_free(data);
|
||||||
|
|
|
@ -2113,6 +2113,7 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
|
||||||
|
|
||||||
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
||||||
size_t pmk_len, const u8 *snonce, const u8 *anonce,
|
size_t pmk_len, const u8 *snonce, const u8 *anonce,
|
||||||
|
const u8 *dhss, size_t dhss_len,
|
||||||
struct wpabuf *g_sta, struct wpabuf *g_ap)
|
struct wpabuf *g_sta, struct wpabuf *g_ap)
|
||||||
{
|
{
|
||||||
u8 ick[FILS_ICK_MAX_LEN];
|
u8 ick[FILS_ICK_MAX_LEN];
|
||||||
|
@ -2122,7 +2123,8 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
||||||
size_t fils_ft_len = 0;
|
size_t fils_ft_len = 0;
|
||||||
|
|
||||||
res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
|
res = fils_pmk_to_ptk(pmk, pmk_len, sm->addr, sm->wpa_auth->addr,
|
||||||
snonce, anonce, &sm->PTK, ick, &ick_len,
|
snonce, anonce, dhss, dhss_len,
|
||||||
|
&sm->PTK, ick, &ick_len,
|
||||||
sm->wpa_key_mgmt, sm->pairwise,
|
sm->wpa_key_mgmt, sm->pairwise,
|
||||||
fils_ft, &fils_ft_len);
|
fils_ft, &fils_ft_len);
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
|
|
|
@ -400,6 +400,7 @@ int wpa_auth_ensure_group(struct wpa_authenticator *wpa_auth, int vlan_id);
|
||||||
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
|
int wpa_auth_release_group(struct wpa_authenticator *wpa_auth, int vlan_id);
|
||||||
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
|
||||||
size_t pmk_len, const u8 *snonce, const u8 *anonce,
|
size_t pmk_len, const u8 *snonce, const u8 *anonce,
|
||||||
|
const u8 *dhss, size_t dhss_len,
|
||||||
struct wpabuf *g_sta, struct wpabuf *g_ap);
|
struct wpabuf *g_sta, struct wpabuf *g_ap);
|
||||||
int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
|
int fils_decrypt_assoc(struct wpa_state_machine *sm, const u8 *fils_session,
|
||||||
const struct ieee80211_mgmt *mgmt, size_t frame_len,
|
const struct ieee80211_mgmt *mgmt, size_t frame_len,
|
||||||
|
|
|
@ -389,19 +389,22 @@ int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
|
||||||
|
|
||||||
|
|
||||||
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
|
const u8 *snonce, const u8 *anonce, const u8 *dhss,
|
||||||
|
size_t dhss_len, struct wpa_ptk *ptk,
|
||||||
u8 *ick, size_t *ick_len, int akmp, int cipher,
|
u8 *ick, size_t *ick_len, int akmp, int cipher,
|
||||||
u8 *fils_ft, size_t *fils_ft_len)
|
u8 *fils_ft, size_t *fils_ft_len)
|
||||||
{
|
{
|
||||||
u8 data[2 * ETH_ALEN + 2 * FILS_NONCE_LEN];
|
u8 *data, *pos;
|
||||||
|
size_t data_len;
|
||||||
u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
|
u8 tmp[FILS_ICK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN +
|
||||||
FILS_FT_MAX_LEN];
|
FILS_FT_MAX_LEN];
|
||||||
size_t key_data_len;
|
size_t key_data_len;
|
||||||
const char *label = "FILS PTK Derivation";
|
const char *label = "FILS PTK Derivation";
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
|
* FILS-Key-Data = PRF-X(PMK, "FILS PTK Derivation",
|
||||||
* SPA || AA || SNonce || ANonce)
|
* SPA || AA || SNonce || ANonce [ || DHss ])
|
||||||
* ICK = L(FILS-Key-Data, 0, ICK_bits)
|
* ICK = L(FILS-Key-Data, 0, ICK_bits)
|
||||||
* KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
|
* KEK = L(FILS-Key-Data, ICK_bits, KEK_bits)
|
||||||
* TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
|
* TK = L(FILS-Key-Data, ICK_bits + KEK_bits, TK_bits)
|
||||||
|
@ -409,10 +412,21 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
* FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
|
* FILS-FT = L(FILS-Key-Data, ICK_bits + KEK_bits + TK_bits,
|
||||||
* FILS-FT_bits)
|
* FILS-FT_bits)
|
||||||
*/
|
*/
|
||||||
os_memcpy(data, spa, ETH_ALEN);
|
data_len = 2 * ETH_ALEN + 2 * FILS_NONCE_LEN + dhss_len;
|
||||||
os_memcpy(data + ETH_ALEN, aa, ETH_ALEN);
|
data = os_malloc(data_len);
|
||||||
os_memcpy(data + 2 * ETH_ALEN, snonce, FILS_NONCE_LEN);
|
if (!data)
|
||||||
os_memcpy(data + 2 * ETH_ALEN + FILS_NONCE_LEN, anonce, FILS_NONCE_LEN);
|
goto err;
|
||||||
|
pos = data;
|
||||||
|
os_memcpy(pos, spa, ETH_ALEN);
|
||||||
|
pos += ETH_ALEN;
|
||||||
|
os_memcpy(pos, aa, ETH_ALEN);
|
||||||
|
pos += ETH_ALEN;
|
||||||
|
os_memcpy(pos, snonce, FILS_NONCE_LEN);
|
||||||
|
pos += FILS_NONCE_LEN;
|
||||||
|
os_memcpy(pos, anonce, FILS_NONCE_LEN);
|
||||||
|
pos += FILS_NONCE_LEN;
|
||||||
|
if (dhss)
|
||||||
|
os_memcpy(pos, dhss, dhss_len);
|
||||||
|
|
||||||
ptk->kck_len = 0;
|
ptk->kck_len = 0;
|
||||||
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
|
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
|
||||||
|
@ -422,7 +436,7 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
else if (wpa_key_mgmt_sha256(akmp))
|
else if (wpa_key_mgmt_sha256(akmp))
|
||||||
*ick_len = 32;
|
*ick_len = 32;
|
||||||
else
|
else
|
||||||
return -1;
|
goto err;
|
||||||
key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
|
key_data_len = *ick_len + ptk->kek_len + ptk->tk_len;
|
||||||
|
|
||||||
if (fils_ft && fils_ft_len) {
|
if (fils_ft && fils_ft_len) {
|
||||||
|
@ -439,20 +453,22 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
|
|
||||||
if (wpa_key_mgmt_sha384(akmp)) {
|
if (wpa_key_mgmt_sha384(akmp)) {
|
||||||
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
|
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA384)");
|
||||||
if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
|
if (sha384_prf(pmk, pmk_len, label, data, data_len,
|
||||||
tmp, key_data_len) < 0)
|
tmp, key_data_len) < 0)
|
||||||
return -1;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
|
wpa_printf(MSG_DEBUG, "FILS: PTK derivation using PRF(SHA256)");
|
||||||
if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
|
if (sha256_prf(pmk, pmk_len, label, data, data_len,
|
||||||
tmp, key_data_len) < 0)
|
tmp, key_data_len) < 0)
|
||||||
return -1;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
|
wpa_printf(MSG_DEBUG, "FILS: PTK derivation - SPA=" MACSTR
|
||||||
" AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
|
" AA=" MACSTR, MAC2STR(spa), MAC2STR(aa));
|
||||||
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
|
wpa_hexdump(MSG_DEBUG, "FILS: SNonce", snonce, FILS_NONCE_LEN);
|
||||||
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
|
wpa_hexdump(MSG_DEBUG, "FILS: ANonce", anonce, FILS_NONCE_LEN);
|
||||||
|
if (dhss)
|
||||||
|
wpa_hexdump_key(MSG_DEBUG, "FILS: DHss", dhss, dhss_len);
|
||||||
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
|
wpa_hexdump_key(MSG_DEBUG, "FILS: PMK", pmk, pmk_len);
|
||||||
wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
|
wpa_hexdump_key(MSG_DEBUG, "FILS: FILS-Key-Data", tmp, key_data_len);
|
||||||
|
|
||||||
|
@ -473,7 +489,10 @@ int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
}
|
}
|
||||||
|
|
||||||
os_memset(tmp, 0, sizeof(tmp));
|
os_memset(tmp, 0, sizeof(tmp));
|
||||||
return 0;
|
ret = 0;
|
||||||
|
err:
|
||||||
|
bin_clear_free(data, data_len);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -347,7 +347,8 @@ int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
|
||||||
int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
|
int fils_pmkid_erp(int akmp, const u8 *reauth, size_t reauth_len,
|
||||||
u8 *pmkid);
|
u8 *pmkid);
|
||||||
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
int fils_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const u8 *spa, const u8 *aa,
|
||||||
const u8 *snonce, const u8 *anonce, struct wpa_ptk *ptk,
|
const u8 *snonce, const u8 *anonce, const u8 *dhss,
|
||||||
|
size_t dhss_len, struct wpa_ptk *ptk,
|
||||||
u8 *ick, size_t *ick_len, int akmp, int cipher,
|
u8 *ick, size_t *ick_len, int akmp, int cipher,
|
||||||
u8 *fils_ft, size_t *fils_ft_len);
|
u8 *fils_ft, size_t *fils_ft_len);
|
||||||
int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
|
int fils_key_auth_sk(const u8 *ick, size_t ick_len, const u8 *snonce,
|
||||||
|
|
|
@ -3639,8 +3639,12 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
|
||||||
dh_ss ? wpabuf_len(dh_ss) : 0,
|
dh_ss ? wpabuf_len(dh_ss) : 0,
|
||||||
sm->pmk, &sm->pmk_len);
|
sm->pmk, &sm->pmk_len);
|
||||||
os_memset(rmsk, 0, sizeof(rmsk));
|
os_memset(rmsk, 0, sizeof(rmsk));
|
||||||
|
|
||||||
|
/* Don't use DHss in PTK derivation if PMKSA caching is not
|
||||||
|
* used. */
|
||||||
wpabuf_clear_free(dh_ss);
|
wpabuf_clear_free(dh_ss);
|
||||||
dh_ss = NULL;
|
dh_ss = NULL;
|
||||||
|
|
||||||
if (res)
|
if (res)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
|
@ -3665,12 +3669,19 @@ int fils_process_auth(struct wpa_sm *sm, const u8 *bssid, const u8 *data,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
|
if (fils_pmk_to_ptk(sm->pmk, sm->pmk_len, sm->own_addr, sm->bssid,
|
||||||
sm->fils_nonce, sm->fils_anonce, &sm->ptk,
|
sm->fils_nonce, sm->fils_anonce,
|
||||||
ick, &ick_len, sm->key_mgmt, sm->pairwise_cipher,
|
dh_ss ? wpabuf_head(dh_ss) : NULL,
|
||||||
|
dh_ss ? wpabuf_len(dh_ss) : 0,
|
||||||
|
&sm->ptk, ick, &ick_len,
|
||||||
|
sm->key_mgmt, sm->pairwise_cipher,
|
||||||
sm->fils_ft, &sm->fils_ft_len) < 0) {
|
sm->fils_ft, &sm->fils_ft_len) < 0) {
|
||||||
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
|
wpa_printf(MSG_DEBUG, "FILS: Failed to derive PTK");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wpabuf_clear_free(dh_ss);
|
||||||
|
dh_ss = NULL;
|
||||||
|
|
||||||
sm->ptk_set = 1;
|
sm->ptk_set = 1;
|
||||||
sm->tptk_set = 0;
|
sm->tptk_set = 0;
|
||||||
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
|
os_memset(&sm->tptk, 0, sizeof(sm->tptk));
|
||||||
|
|
|
@ -351,7 +351,8 @@ static int try_rmsk(struct wlantest *wt, struct wlantest_bss *bss,
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (fils_pmk_to_ptk(pmk_buf, pmk_len, sta->addr, bss->bssid,
|
if (fils_pmk_to_ptk(pmk_buf, pmk_len, sta->addr, bss->bssid,
|
||||||
sta->snonce, sta->anonce, &ptk, ick, &ick_len,
|
sta->snonce, sta->anonce, NULL, 0,
|
||||||
|
&ptk, ick, &ick_len,
|
||||||
sta->key_mgmt, sta->pairwise_cipher,
|
sta->key_mgmt, sta->pairwise_cipher,
|
||||||
NULL, NULL) < 0)
|
NULL, NULL) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
Loading…
Reference in a new issue