wlantest: Derive PMK-R1 and PTK for FT protocol cases
Track PMK-R0/PMK-R0-Name from the initial mobility domain association and derive PMK-R1/PTK when the station uses FT protocol. This allows frames from additional roaming cases to be decrypted. Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
parent
dbddbf1647
commit
c38c62ff78
5 changed files with 240 additions and 10 deletions
|
@ -990,9 +990,11 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
|
|||
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
|
||||
ftie_sha384->mic,
|
||||
sizeof(ftie_sha384->mic));
|
||||
parse->fte_anonce = ftie_sha384->anonce;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
|
||||
ftie_sha384->anonce,
|
||||
WPA_NONCE_LEN);
|
||||
parse->fte_snonce = ftie_sha384->snonce;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
|
||||
ftie_sha384->snonce,
|
||||
WPA_NONCE_LEN);
|
||||
|
@ -1009,8 +1011,10 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
|
|||
ftie->mic_control, 2);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: FTE-MIC",
|
||||
ftie->mic, sizeof(ftie->mic));
|
||||
parse->fte_anonce = ftie->anonce;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: FTE-ANonce",
|
||||
ftie->anonce, WPA_NONCE_LEN);
|
||||
parse->fte_snonce = ftie->snonce;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: FTE-SNonce",
|
||||
ftie->snonce, WPA_NONCE_LEN);
|
||||
prot_ie_count = ftie->mic_control[1];
|
||||
|
|
|
@ -451,6 +451,8 @@ struct wpa_ft_ies {
|
|||
size_t gtk_len;
|
||||
const u8 *r0kh_id;
|
||||
size_t r0kh_id_len;
|
||||
const u8 *fte_anonce;
|
||||
const u8 *fte_snonce;
|
||||
const u8 *rsn;
|
||||
size_t rsn_len;
|
||||
u16 rsn_capab;
|
||||
|
|
|
@ -100,21 +100,23 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
|
|||
struct wpa_ptk ptk;
|
||||
|
||||
if (wpa_key_mgmt_ft(sta->key_mgmt)) {
|
||||
u8 pmk_r0[PMK_LEN];
|
||||
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
|
||||
u8 pmk_r1[PMK_LEN];
|
||||
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
||||
u8 ptk_name[WPA_PMK_NAME_LEN];
|
||||
|
||||
wpa_derive_pmk_r0(pmk->pmk, PMK_LEN,
|
||||
if (wpa_derive_pmk_r0(pmk->pmk, PMK_LEN,
|
||||
bss->ssid, bss->ssid_len, bss->mdid,
|
||||
bss->r0kh_id, bss->r0kh_id_len,
|
||||
sta->addr, pmk_r0, pmk_r0_name, 0);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name,
|
||||
sta->addr, sta->pmk_r0, sta->pmk_r0_name,
|
||||
0) < 0)
|
||||
return -1;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMK-R0", sta->pmk_r0, PMK_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sta->pmk_r0_name,
|
||||
WPA_PMK_NAME_LEN);
|
||||
wpa_derive_pmk_r1(pmk_r0, PMK_LEN, pmk_r0_name, bss->r1kh_id,
|
||||
sta->addr, pmk_r1, pmk_r1_name);
|
||||
if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name,
|
||||
bss->r1kh_id, sta->addr,
|
||||
pmk_r1, pmk_r1_name) < 0)
|
||||
return -1;
|
||||
wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name,
|
||||
WPA_PMK_NAME_LEN);
|
||||
|
|
|
@ -162,6 +162,81 @@ static void process_fils_auth(struct wlantest *wt, struct wlantest_bss *bss,
|
|||
}
|
||||
|
||||
|
||||
static void process_ft_auth(struct wlantest *wt, struct wlantest_bss *bss,
|
||||
struct wlantest_sta *sta,
|
||||
const struct ieee80211_mgmt *mgmt, size_t len)
|
||||
{
|
||||
u16 trans;
|
||||
struct wpa_ft_ies parse;
|
||||
u8 pmk_r1[PMK_LEN];
|
||||
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
||||
struct wpa_ptk ptk;
|
||||
u8 ptk_name[WPA_PMK_NAME_LEN];
|
||||
struct wlantest_bss *old_bss;
|
||||
struct wlantest_sta *old_sta = NULL;
|
||||
|
||||
if (sta->auth_alg != WLAN_AUTH_FT ||
|
||||
len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
|
||||
return;
|
||||
|
||||
trans = le_to_host16(mgmt->u.auth.auth_transaction);
|
||||
|
||||
if (wpa_ft_parse_ies(mgmt->u.auth.variable,
|
||||
len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
|
||||
&parse, -1)) {
|
||||
add_note(wt, MSG_INFO,
|
||||
"Could not parse FT Authentication Response frame");
|
||||
return;
|
||||
}
|
||||
|
||||
if (trans == 1) {
|
||||
sta->key_mgmt = parse.key_mgmt;
|
||||
sta->pairwise_cipher = parse.pairwise_cipher;
|
||||
return;
|
||||
}
|
||||
|
||||
if (trans != 2)
|
||||
return;
|
||||
|
||||
/* TODO: Should find the latest updated PMK-R0 value here instead
|
||||
* copying the one from the first found matching old STA entry. */
|
||||
dl_list_for_each(old_bss, &wt->bss, struct wlantest_bss, list) {
|
||||
if (old_bss == bss)
|
||||
continue;
|
||||
old_sta = sta_find(old_bss, sta->addr);
|
||||
if (old_sta)
|
||||
break;
|
||||
}
|
||||
if (!old_sta)
|
||||
return;
|
||||
|
||||
os_memcpy(sta->pmk_r0, old_sta->pmk_r0, sizeof(sta->pmk_r0));
|
||||
os_memcpy(sta->pmk_r0_name, old_sta->pmk_r0_name,
|
||||
sizeof(sta->pmk_r0_name));
|
||||
|
||||
if (parse.r1kh_id)
|
||||
os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
|
||||
|
||||
if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name,
|
||||
bss->r1kh_id, sta->addr, pmk_r1, pmk_r1_name) < 0)
|
||||
return;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
|
||||
|
||||
if (!parse.fte_anonce || !parse.fte_snonce ||
|
||||
wpa_pmk_r1_to_ptk(pmk_r1, PMK_LEN, parse.fte_snonce,
|
||||
parse.fte_anonce, sta->addr, bss->bssid,
|
||||
pmk_r1_name, &ptk, ptk_name, sta->key_mgmt,
|
||||
sta->pairwise_cipher) < 0)
|
||||
return;
|
||||
|
||||
add_note(wt, MSG_DEBUG, "Derived new PTK");
|
||||
os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
|
||||
sta->ptk_set = 1;
|
||||
os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
|
||||
os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
|
||||
}
|
||||
|
||||
|
||||
static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
|
||||
{
|
||||
const struct ieee80211_mgmt *mgmt;
|
||||
|
@ -210,6 +285,7 @@ static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
|
|||
sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++;
|
||||
|
||||
process_fils_auth(wt, bss, sta, mgmt, len);
|
||||
process_ft_auth(wt, bss, sta, mgmt, len);
|
||||
}
|
||||
|
||||
|
||||
|
@ -736,6 +812,8 @@ static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
|
|||
sta->assocreq_ies_len);
|
||||
|
||||
sta_update_assoc(sta, &elems);
|
||||
|
||||
/* TODO: FT protocol: verify FTE MIC and update GTK/IGTK for the BSS */
|
||||
}
|
||||
|
||||
|
||||
|
@ -928,6 +1006,145 @@ static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
|
|||
}
|
||||
|
||||
|
||||
static void rx_mgmt_action_ft_request(struct wlantest *wt,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
const u8 *ies;
|
||||
size_t ies_len;
|
||||
struct wpa_ft_ies parse;
|
||||
struct wlantest_bss *bss;
|
||||
struct wlantest_sta *sta;
|
||||
|
||||
if (len < 24 + 2 + 2 * ETH_ALEN) {
|
||||
add_note(wt, MSG_INFO, "Too short FT Request frame");
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT Request: STA Address: " MACSTR
|
||||
" Target AP Address: " MACSTR,
|
||||
MAC2STR(mgmt->u.action.u.ft_action_req.sta_addr),
|
||||
MAC2STR(mgmt->u.action.u.ft_action_req.target_ap_addr));
|
||||
ies = mgmt->u.action.u.ft_action_req.variable;
|
||||
ies_len = len - (24 + 2 + 2 * ETH_ALEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FT Request frame body", ies, ies_len);
|
||||
|
||||
if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
|
||||
add_note(wt, MSG_INFO, "Could not parse FT Request frame body");
|
||||
return;
|
||||
}
|
||||
|
||||
bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
|
||||
if (!bss) {
|
||||
add_note(wt, MSG_INFO, "No BSS entry for Target AP");
|
||||
return;
|
||||
}
|
||||
|
||||
sta = sta_get(bss, mgmt->sa);
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
sta->key_mgmt = parse.key_mgmt;
|
||||
sta->pairwise_cipher = parse.pairwise_cipher;
|
||||
}
|
||||
|
||||
|
||||
static void rx_mgmt_action_ft_response(struct wlantest *wt,
|
||||
struct wlantest_sta *sta,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len)
|
||||
{
|
||||
struct wlantest_bss *bss;
|
||||
struct wlantest_sta *new_sta;
|
||||
const u8 *ies;
|
||||
size_t ies_len;
|
||||
struct wpa_ft_ies parse;
|
||||
u8 pmk_r1[PMK_LEN];
|
||||
u8 pmk_r1_name[WPA_PMK_NAME_LEN];
|
||||
struct wpa_ptk ptk;
|
||||
u8 ptk_name[WPA_PMK_NAME_LEN];
|
||||
|
||||
if (len < 24 + 2 + 2 * ETH_ALEN + 2) {
|
||||
add_note(wt, MSG_INFO, "Too short FT Response frame from "
|
||||
MACSTR, MAC2STR(mgmt->sa));
|
||||
return;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "FT Response: STA Address: " MACSTR
|
||||
" Target AP Address: " MACSTR " Status Code: %u",
|
||||
MAC2STR(mgmt->u.action.u.ft_action_resp.sta_addr),
|
||||
MAC2STR(mgmt->u.action.u.ft_action_resp.target_ap_addr),
|
||||
le_to_host16(mgmt->u.action.u.ft_action_resp.status_code));
|
||||
ies = mgmt->u.action.u.ft_action_req.variable;
|
||||
ies_len = len - (24 + 2 + 2 * ETH_ALEN);
|
||||
wpa_hexdump(MSG_DEBUG, "FT Response frame body", ies, ies_len);
|
||||
|
||||
if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
|
||||
add_note(wt, MSG_INFO,
|
||||
"Could not parse FT Response frame body");
|
||||
return;
|
||||
}
|
||||
|
||||
bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
|
||||
if (!bss) {
|
||||
add_note(wt, MSG_INFO, "No BSS entry for Target AP");
|
||||
return;
|
||||
}
|
||||
|
||||
if (parse.r1kh_id)
|
||||
os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
|
||||
|
||||
if (wpa_derive_pmk_r1(sta->pmk_r0, PMK_LEN, sta->pmk_r0_name,
|
||||
bss->r1kh_id, sta->addr, pmk_r1, pmk_r1_name) < 0)
|
||||
return;
|
||||
wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN);
|
||||
|
||||
new_sta = sta_get(bss, sta->addr);
|
||||
if (!new_sta)
|
||||
return;
|
||||
os_memcpy(new_sta->pmk_r0, sta->pmk_r0, sizeof(sta->pmk_r0));
|
||||
os_memcpy(new_sta->pmk_r0_name, sta->pmk_r0_name,
|
||||
sizeof(sta->pmk_r0_name));
|
||||
if (!parse.fte_anonce || !parse.fte_snonce ||
|
||||
wpa_pmk_r1_to_ptk(pmk_r1, PMK_LEN, parse.fte_snonce,
|
||||
parse.fte_anonce, new_sta->addr, bss->bssid,
|
||||
pmk_r1_name, &ptk, ptk_name, new_sta->key_mgmt,
|
||||
new_sta->pairwise_cipher) < 0)
|
||||
return;
|
||||
|
||||
add_note(wt, MSG_DEBUG, "Derived new PTK");
|
||||
os_memcpy(&new_sta->ptk, &ptk, sizeof(ptk));
|
||||
new_sta->ptk_set = 1;
|
||||
os_memset(new_sta->rsc_tods, 0, sizeof(new_sta->rsc_tods));
|
||||
os_memset(new_sta->rsc_fromds, 0, sizeof(new_sta->rsc_fromds));
|
||||
}
|
||||
|
||||
|
||||
static void rx_mgmt_action_ft(struct wlantest *wt, struct wlantest_sta *sta,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
size_t len, int valid)
|
||||
{
|
||||
if (len < 24 + 2) {
|
||||
add_note(wt, MSG_INFO, "Too short FT Action frame from " MACSTR,
|
||||
MAC2STR(mgmt->sa));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (mgmt->u.action.u.ft_action_req.action) {
|
||||
case 1:
|
||||
rx_mgmt_action_ft_request(wt, mgmt, len);
|
||||
break;
|
||||
case 2:
|
||||
rx_mgmt_action_ft_response(wt, sta, mgmt, len);
|
||||
break;
|
||||
default:
|
||||
add_note(wt, MSG_INFO, "Unsupported FT action value %u from "
|
||||
MACSTR, mgmt->u.action.u.ft_action_req.action,
|
||||
MAC2STR(mgmt->sa));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void rx_mgmt_action_sa_query_req(struct wlantest *wt,
|
||||
struct wlantest_sta *sta,
|
||||
const struct ieee80211_mgmt *mgmt,
|
||||
|
@ -1070,6 +1287,9 @@ static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
|
|||
}
|
||||
|
||||
switch (mgmt->u.action.category) {
|
||||
case WLAN_ACTION_FT:
|
||||
rx_mgmt_action_ft(wt, sta, mgmt, len, valid);
|
||||
break;
|
||||
case WLAN_ACTION_SA_QUERY:
|
||||
rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid);
|
||||
break;
|
||||
|
|
|
@ -71,6 +71,8 @@ struct wlantest_sta {
|
|||
int rsn_capab;
|
||||
u8 anonce[32]; /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
|
||||
u8 snonce[32]; /* SNonce from the previous EAPOL-Key msg 2/4 */
|
||||
u8 pmk_r0[PMK_LEN];
|
||||
u8 pmk_r0_name[WPA_PMK_NAME_LEN];
|
||||
struct wpa_ptk ptk; /* Derived PTK */
|
||||
int ptk_set;
|
||||
struct wpa_ptk tptk; /* Derived PTK during rekeying */
|
||||
|
|
Loading…
Reference in a new issue