Skip to content

Commit

Permalink
wifi: iwlwifi: mvm: update two most recent GTKs on D3 resume flow
Browse files Browse the repository at this point in the history
When resuming from D3 the two most recent GTKs are passed from
the FW with wowlan_info_notif. Both keys should be updated as
they both might be needed upon FW restart and they both should
be removed upon station removal.

Signed-off-by: Yedidya Benshimol <yedidya.ben.shimol@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230621144844.3ea3a9f52ec2.I7cedfa2bb0eafb83e7c77363673560acf05bff74@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Yedidya Benshimol authored and Johannes Berg committed Jun 21, 2023
1 parent 60555ea commit fa4e48f
Showing 1 changed file with 115 additions and 81 deletions.
196 changes: 115 additions & 81 deletions drivers/net/wireless/intel/iwlwifi/mvm/d3.c
Original file line number Diff line number Diff line change
Expand Up @@ -1398,7 +1398,8 @@ struct iwl_wowlan_status_data {
u8 key[WOWLAN_KEY_MAX_SIZE];
u8 len;
u8 flags;
} gtk;
u8 id;
} gtk[WOWLAN_GTK_KEYS_NUM];

struct {
/*
Expand Down Expand Up @@ -1758,7 +1759,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
s8 new_key_id = -1;

if (status->num_of_gtk_rekeys)
new_key_id = status->gtk.flags &
new_key_id = status->gtk[0].flags &
IWL_WOWLAN_GTK_IDX_MASK;

/* Don't install a new key's value to an old key */
Expand All @@ -1777,8 +1778,7 @@ static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key,
struct iwl_mvm_d3_gtk_iter_data {
struct iwl_mvm *mvm;
struct iwl_wowlan_status_data *status;
void *last_gtk;
u32 cipher;
u32 gtk_cipher;
bool unhandled_cipher;
int num_keys;
};
Expand All @@ -1804,8 +1804,7 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw,
case WLAN_CIPHER_SUITE_GCMP_256:
case WLAN_CIPHER_SUITE_TKIP:
/* we support these */
data->last_gtk = key;
data->cipher = key->cipher;
data->gtk_cipher = key->cipher;
break;
default:
/* everything else - disconnect from AP */
Expand All @@ -1824,6 +1823,7 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
{
struct iwl_mvm_d3_gtk_iter_data *data = _data;
struct iwl_wowlan_status_data *status = data->status;
s8 keyidx;

if (data->unhandled_cipher)
return;
Expand All @@ -1848,14 +1848,76 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
iwl_mvm_set_key_rx_seq_tids(key, status->ptk.tkip.seq);
return;
}
if (data->status->num_of_gtk_rekeys)
keyidx = key->keyidx;
/* The current key is always sent by the FW, even if it wasn't
* rekeyed during D3.
* We remove an existing key if it has the same index as
* a new key
*/
if (status->num_of_gtk_rekeys &&
((status->gtk[0].len && keyidx == status->gtk[0].id) ||
(status->gtk[1].len && keyidx == status->gtk[1].id))) {
ieee80211_remove_key(key);

if (data->last_gtk == key)
} else {
iwl_mvm_set_key_rx_seq(key, data->status, false);
}
}
}

static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
struct ieee80211_vif *vif,
struct iwl_mvm *mvm, u32 gtk_cipher)
{
int i;
struct ieee80211_key_conf *key;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {
.conf.cipher = gtk_cipher,
};

BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
BUILD_BUG_ON(sizeof(conf.key) < sizeof(status->gtk[0].key));

switch (gtk_cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
break;
case WLAN_CIPHER_SUITE_TKIP:
conf.conf.keylen = WLAN_KEY_LEN_TKIP;
break;
default:
WARN_ON(1);
}

for (i = 0; i < ARRAY_SIZE(status->gtk); i++) {
if (!status->gtk[i].len)
continue;

conf.conf.keyidx = status->gtk[i].id;
IWL_DEBUG_WOWLAN(mvm,
"Received from FW GTK cipher %d, key index %d\n",
conf.conf.cipher, conf.conf.keyidx);
memcpy(conf.conf.key, status->gtk[i].key,
sizeof(status->gtk[i].key));

key = ieee80211_gtk_rekey_add(vif, &conf.conf);
if (IS_ERR(key))
return false;
iwl_mvm_set_key_rx_seq_idx(key, status, i);
}

return true;
}

static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_wowlan_status_data *status)
Expand Down Expand Up @@ -1883,8 +1945,6 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
return false;
if (!gtkdata.num_keys)
goto out;
if (!gtkdata.last_gtk)
return false;

/*
* invalidate all other GTKs that might still exist and update
Expand All @@ -1893,52 +1953,14 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_d3_update_keys, &gtkdata);

IWL_DEBUG_WOWLAN(mvm, "num of GTK rekeying %d\n",
status->num_of_gtk_rekeys);
if (status->num_of_gtk_rekeys) {
struct ieee80211_key_conf *key;
struct {
struct ieee80211_key_conf conf;
u8 key[32];
} conf = {
.conf.cipher = gtkdata.cipher,
.conf.keyidx =
status->gtk.flags & IWL_WOWLAN_GTK_IDX_MASK,
};
__be64 replay_ctr;

IWL_DEBUG_WOWLAN(mvm,
"Received from FW GTK cipher %d, key index %d\n",
conf.conf.cipher, conf.conf.keyidx);
__be64 replay_ctr = cpu_to_be64(status->replay_ctr);

BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256);
BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP);
BUILD_BUG_ON(sizeof(conf.key) < sizeof(status->gtk.key));
IWL_DEBUG_WOWLAN(mvm, "num of GTK rekeying %d\n",
status->num_of_gtk_rekeys);

memcpy(conf.conf.key, status->gtk.key, sizeof(status->gtk.key));

switch (gtkdata.cipher) {
case WLAN_CIPHER_SUITE_CCMP:
case WLAN_CIPHER_SUITE_GCMP:
conf.conf.keylen = WLAN_KEY_LEN_CCMP;
break;
case WLAN_CIPHER_SUITE_GCMP_256:
conf.conf.keylen = WLAN_KEY_LEN_GCMP_256;
break;
case WLAN_CIPHER_SUITE_TKIP:
conf.conf.keylen = WLAN_KEY_LEN_TKIP;
break;
}

key = ieee80211_gtk_rekey_add(vif, &conf.conf);
if (IS_ERR(key))
if (!iwl_mvm_gtk_rekey(status, vif, mvm, gtkdata.gtk_cipher))
return false;
iwl_mvm_set_key_rx_seq(key, status, true);

replay_ctr = cpu_to_be64(status->replay_ctr);

ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
(void *)&replay_ctr, GFP_KERNEL);
}
Expand All @@ -1957,40 +1979,52 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
static void iwl_mvm_convert_gtk_v2(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_gtk_status_v2 *data)
{
BUILD_BUG_ON(sizeof(status->gtk.key) < sizeof(data->key));
BUILD_BUG_ON(sizeof(status->gtk[0].key) < sizeof(data->key));
BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
sizeof(data->tkip_mic_key) >
sizeof(status->gtk.key));
sizeof(status->gtk[0].key));

status->gtk.len = data->key_len;
status->gtk.flags = data->key_flags;
status->gtk[0].len = data->key_len;
status->gtk[0].flags = data->key_flags;

memcpy(status->gtk.key, data->key, sizeof(data->key));
memcpy(status->gtk[0].key, data->key, sizeof(data->key));

/* if it's as long as the TKIP encryption key, copy MIC key */
if (status->gtk.len == NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
memcpy(status->gtk.key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
if (status->gtk[0].len == NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
memcpy(status->gtk[0].key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
data->tkip_mic_key, sizeof(data->tkip_mic_key));
}

static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_gtk_status_v3 *data)
{
/* The parts we need are identical in v2 and v3 */
#define CHECK(_f) do { \
BUILD_BUG_ON(offsetof(struct iwl_wowlan_gtk_status_v2, _f) != \
offsetof(struct iwl_wowlan_gtk_status_v3, _f)); \
BUILD_BUG_ON(offsetofend(struct iwl_wowlan_gtk_status_v2, _f) !=\
offsetofend(struct iwl_wowlan_gtk_status_v3, _f)); \
} while (0)
int data_idx, status_idx = 0;

CHECK(key);
CHECK(key_len);
CHECK(key_flags);
CHECK(tkip_mic_key);
#undef CHECK
BUILD_BUG_ON(sizeof(status->gtk[0].key) < sizeof(data[0].key));
BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
sizeof(data[0].tkip_mic_key) >
sizeof(status->gtk[0].key));
BUILD_BUG_ON(ARRAY_SIZE(status->gtk) < WOWLAN_GTK_KEYS_NUM);
for (data_idx = 0; data_idx < ARRAY_SIZE(status->gtk); data_idx++) {
if (!(data[data_idx].key_len))
continue;
status->gtk[status_idx].len = data[data_idx].key_len;
status->gtk[status_idx].flags = data[data_idx].key_flags;
status->gtk[status_idx].id = status->gtk[status_idx].flags &
IWL_WOWLAN_GTK_IDX_MASK;

memcpy(status->gtk[status_idx].key, data[data_idx].key,
sizeof(data[data_idx].key));

iwl_mvm_convert_gtk_v2(status, (void *)data);
/* if it's as long as the TKIP encryption key, copy MIC key */
if (status->gtk[status_idx].len ==
NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY)
memcpy(status->gtk[status_idx].key +
NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
data[data_idx].tkip_mic_key,
sizeof(data[data_idx].tkip_mic_key));
status_idx++;
}
}

static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status,
Expand Down Expand Up @@ -2033,7 +2067,7 @@ static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
}

iwl_mvm_convert_key_counters_v5(status, &data->gtk[0].sc);
iwl_mvm_convert_gtk_v3(status, &data->gtk[0]);
iwl_mvm_convert_gtk_v3(status, data->gtk);
iwl_mvm_convert_igtk(status, &data->igtk[0]);

status->replay_ctr = le64_to_cpu(data->replay_ctr);
Expand Down Expand Up @@ -2156,29 +2190,29 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id)
goto out_free_resp;

BUILD_BUG_ON(sizeof(v6->gtk.decrypt_key) >
sizeof(status->gtk.key));
sizeof(status->gtk[0].key));
BUILD_BUG_ON(NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY +
sizeof(v6->gtk.tkip_mic_key) >
sizeof(status->gtk.key));
sizeof(status->gtk[0].key));

/* copy GTK info to the right place */
memcpy(status->gtk.key, v6->gtk.decrypt_key,
memcpy(status->gtk[0].key, v6->gtk.decrypt_key,
sizeof(v6->gtk.decrypt_key));
memcpy(status->gtk.key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
memcpy(status->gtk[0].key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY,
v6->gtk.tkip_mic_key,
sizeof(v6->gtk.tkip_mic_key));

iwl_mvm_convert_key_counters(status, &v6->gtk.rsc.all_tsc_rsc);

/* hardcode the key length to 16 since v6 only supports 16 */
status->gtk.len = 16;
status->gtk[0].len = 16;

/*
* The key index only uses 2 bits (values 0 to 3) and
* we always set bit 7 which means this is the
* currently used key.
*/
status->gtk.flags = v6->gtk.key_index | BIT(7);
status->gtk[0].flags = v6->gtk.key_index | BIT(7);
} else if (notif_ver == 7) {
struct iwl_wowlan_status_v7 *v7 = (void *)cmd.resp_pkt->data;

Expand Down Expand Up @@ -2212,7 +2246,7 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id)
goto out_free_resp;

iwl_mvm_convert_key_counters_v5(status, &v12->gtk[0].sc);
iwl_mvm_convert_gtk_v3(status, &v12->gtk[0]);
iwl_mvm_convert_gtk_v3(status, v12->gtk);
iwl_mvm_convert_igtk(status, &v12->igtk[0]);

status->tid_tear_down = v12->tid_tear_down;
Expand Down

0 comments on commit fa4e48f

Please sign in to comment.