Skip to content

Commit

Permalink
wifi: iwlwifi: mvm: Add support for IGTK in D3 resume flow
Browse files Browse the repository at this point in the history
As part of the new security API in the FW, all security keys are to
be removed before station removal. Until now IGTK rekey
wasn't supported in the D3 resume flow, and thus the driver might
not know the right key to remove.
If an IGTK was rekeyed during D3 the old IGTK is removed and the
new key is updated. If not, the old key's IPN is updated.
As opposed to GTK, which both the FW and the driver hold it's two
most recent keys, only one IGTK is held.

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.b53c301c07e6.I375277a10a1f756b93d4a343f6664351a80189c5@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 fa4e48f commit 04f78e2
Show file tree
Hide file tree
Showing 2 changed files with 147 additions and 25 deletions.
4 changes: 3 additions & 1 deletion drivers/net/wireless/intel/iwlwifi/fw/api/d3.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
* Copyright (C) 2012-2014, 2018-2022 Intel Corporation
* Copyright (C) 2012-2014, 2018-2023 Intel Corporation
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
Expand Down Expand Up @@ -396,6 +396,7 @@ struct iwl_wowlan_config_cmd {
#define WOWLAN_KEY_MAX_SIZE 32
#define WOWLAN_GTK_KEYS_NUM 2
#define WOWLAN_IGTK_KEYS_NUM 2
#define WOWLAN_IGTK_MIN_INDEX 4

/*
* WOWLAN_TSC_RSC_PARAMS
Expand Down Expand Up @@ -612,6 +613,7 @@ struct iwl_wowlan_gtk_status_v3 {
} __packed; /* WOWLAN_GTK_MATERIAL_VER_3 */

#define IWL_WOWLAN_GTK_IDX_MASK (BIT(0) | BIT(1))
#define IWL_WOWLAN_IGTK_BIGTK_IDX_MASK (BIT(0))

/**
* struct iwl_wowlan_igtk_status - IGTK status
Expand Down
168 changes: 144 additions & 24 deletions drivers/net/wireless/intel/iwlwifi/mvm/d3.c
Original file line number Diff line number Diff line change
Expand Up @@ -1380,6 +1380,14 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
return __iwl_mvm_suspend(hw, wowlan, false);
}

struct iwl_multicast_key_data {
u8 key[WOWLAN_KEY_MAX_SIZE];
u8 len;
u8 flags;
u8 id;
u8 ipn[6];
};

/* converted data from the different status responses */
struct iwl_wowlan_status_data {
u64 replay_ctr;
Expand Down Expand Up @@ -1429,12 +1437,7 @@ struct iwl_wowlan_status_data {
} tkip, aes;
} ptk;

struct {
u64 ipn;
u8 key[WOWLAN_KEY_MAX_SIZE];
u8 len;
u8 flags;
} igtk;
struct iwl_multicast_key_data igtk;

u8 *wake_packet;
};
Expand Down Expand Up @@ -1778,8 +1781,8 @@ 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;
u32 gtk_cipher;
bool unhandled_cipher;
u32 gtk_cipher, igtk_cipher;
bool unhandled_cipher, igtk_support;
int num_keys;
};

Expand All @@ -1806,6 +1809,19 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw,
/* we support these */
data->gtk_cipher = key->cipher;
break;
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
case WLAN_CIPHER_SUITE_AES_CMAC:
/* we support these */
if (data->igtk_support &&
(key->keyidx == 4 || key->keyidx == 5)) {
data->igtk_cipher = key->cipher;
} else {
data->unhandled_cipher = true;
return;
}
break;
default:
/* everything else - disconnect from AP */
data->unhandled_cipher = true;
Expand All @@ -1815,6 +1831,23 @@ static void iwl_mvm_d3_find_last_keys(struct ieee80211_hw *hw,
data->num_keys++;
}

static void
iwl_mvm_d3_set_igtk_bigtk_ipn(const struct iwl_multicast_key_data *key,
struct ieee80211_key_seq *seq, u32 cipher)
{
switch (cipher) {
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
BUILD_BUG_ON(sizeof(seq->aes_gmac.pn) != sizeof(key->ipn));
memcpy(seq->aes_gmac.pn, key->ipn, sizeof(seq->aes_gmac.pn));
break;
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
BUILD_BUG_ON(sizeof(seq->aes_cmac.pn) != sizeof(key->ipn));
memcpy(seq->aes_cmac.pn, key->ipn, sizeof(seq->aes_cmac.pn));
break;
}
}

static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
struct ieee80211_vif *vif,
struct ieee80211_sta *sta,
Expand Down Expand Up @@ -1861,6 +1894,24 @@ static void iwl_mvm_d3_update_keys(struct ieee80211_hw *hw,
} else {
iwl_mvm_set_key_rx_seq(key, data->status, false);
}
break;
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
case WLAN_CIPHER_SUITE_AES_CMAC:
if (key->keyidx == 4 || key->keyidx == 5) {
/* remove rekeyed key */
if (status->num_of_gtk_rekeys) {
ieee80211_remove_key(key);
} else {
struct ieee80211_key_seq seq;

iwl_mvm_d3_set_igtk_bigtk_ipn(&status->igtk,
&seq,
key->cipher);
ieee80211_set_key_rx_seq(key, 0, &seq);
}
}
}
}

Expand Down Expand Up @@ -1918,6 +1969,70 @@ static bool iwl_mvm_gtk_rekey(struct iwl_wowlan_status_data *status,
return true;
}

static bool
iwl_mvm_d3_igtk_bigtk_rekey_add(struct iwl_wowlan_status_data *status,
struct ieee80211_vif *vif, u32 cipher,
struct iwl_multicast_key_data *key_data)
{
struct ieee80211_key_conf *key_config;
struct {
struct ieee80211_key_conf conf;
u8 key[WOWLAN_KEY_MAX_SIZE];
} conf = {
.conf.cipher = cipher,
.conf.keyidx = key_data->id,
};
struct ieee80211_key_seq seq;

if (!key_data->len)
return true;

iwl_mvm_d3_set_igtk_bigtk_ipn(key_data, &seq, conf.conf.cipher);

switch (cipher) {
case WLAN_CIPHER_SUITE_BIP_GMAC_128:
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128;
break;
case WLAN_CIPHER_SUITE_BIP_GMAC_256:
conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256;
break;
case WLAN_CIPHER_SUITE_AES_CMAC:
conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC;
break;
case WLAN_CIPHER_SUITE_BIP_CMAC_256:
conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256;
break;
default:
WARN_ON(1);
}
BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key));
memcpy(conf.conf.key, key_data->key, conf.conf.keylen);

key_config = ieee80211_gtk_rekey_add(vif, &conf.conf);
if (IS_ERR(key_config))
return false;
ieee80211_set_key_rx_seq(key_config, 0, &seq);
return true;
}

static int iwl_mvm_lookup_wowlan_status_ver(struct iwl_mvm *mvm)
{
u8 notif_ver;

if (!fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL))
return 6;

/* default to 7 (when we have IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL) */
notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP,
WOWLAN_GET_STATUSES, 0);
if (!notif_ver)
notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
WOWLAN_GET_STATUSES, 7);

return notif_ver;
}

static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
struct ieee80211_vif *vif,
struct iwl_wowlan_status_data *status)
Expand All @@ -1937,6 +2052,12 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,
if (status->wakeup_reasons & disconnection_reasons)
return false;

if (iwl_mvm_lookup_wowlan_status_ver(mvm) > 6 ||
iwl_fw_lookup_notif_ver(mvm->fw, PROT_OFFLOAD_GROUP,
WOWLAN_INFO_NOTIFICATION,
0))
gtkdata.igtk_support = true;

/* find last GTK that we used initially, if any */
ieee80211_iter_keys(mvm->hw, vif,
iwl_mvm_d3_find_last_keys, &gtkdata);
Expand All @@ -1961,6 +2082,12 @@ static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm,

if (!iwl_mvm_gtk_rekey(status, vif, mvm, gtkdata.gtk_cipher))
return false;

if (!iwl_mvm_d3_igtk_bigtk_rekey_add(status, vif,
gtkdata.igtk_cipher,
&status->igtk))
return false;

ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid,
(void *)&replay_ctr, GFP_KERNEL);
}
Expand Down Expand Up @@ -2030,21 +2157,19 @@ static void iwl_mvm_convert_gtk_v3(struct iwl_wowlan_status_data *status,
static void iwl_mvm_convert_igtk(struct iwl_wowlan_status_data *status,
struct iwl_wowlan_igtk_status *data)
{
const u8 *ipn = data->ipn;

BUILD_BUG_ON(sizeof(status->igtk.key) < sizeof(data->key));

if (!data->key_len)
return;

status->igtk.len = data->key_len;
status->igtk.flags = data->key_flags;
status->igtk.id = u32_get_bits(data->key_flags,
IWL_WOWLAN_IGTK_BIGTK_IDX_MASK)
+ WOWLAN_IGTK_MIN_INDEX;

memcpy(status->igtk.key, data->key, sizeof(data->key));

status->igtk.ipn = ((u64)ipn[5] << 0) |
((u64)ipn[4] << 8) |
((u64)ipn[3] << 16) |
((u64)ipn[2] << 24) |
((u64)ipn[1] << 32) |
((u64)ipn[0] << 40);
memcpy(status->igtk.ipn, data->ipn, sizeof(data->ipn));
}

static void iwl_mvm_parse_wowlan_info_notif(struct iwl_mvm *mvm,
Expand Down Expand Up @@ -2175,14 +2300,9 @@ iwl_mvm_send_wowlan_get_status(struct iwl_mvm *mvm, u8 sta_id)
len = iwl_rx_packet_payload_len(cmd.resp_pkt);

/* default to 7 (when we have IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL) */
notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP,
WOWLAN_GET_STATUSES, 0);
if (!notif_ver)
notif_ver = iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP,
WOWLAN_GET_STATUSES, 7);
notif_ver = iwl_mvm_lookup_wowlan_status_ver(mvm);

if (!fw_has_api(&mvm->fw->ucode_capa,
IWL_UCODE_TLV_API_WOWLAN_KEY_MATERIAL)) {
if (notif_ver < 7) {
struct iwl_wowlan_status_v6 *v6 = (void *)cmd.resp_pkt->data;

status = iwl_mvm_parse_wowlan_status_common_v6(mvm, v6, len);
Expand Down

0 comments on commit 04f78e2

Please sign in to comment.