blob: bbe047c80d885120a4a3cfb14638bdfe533968fc [file] [log] [blame]
/*
* Copyright (c) 2017 The Linux Foundation. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/**
* DOC: Public APIs for crypto service
*/
#include <qdf_types.h>
#include <wlan_cmn.h>
#include <wlan_objmgr_cmn.h>
#include <wlan_objmgr_global_obj.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_objmgr_peer_obj.h>
#include "wlan_crypto_global_def.h"
#include "wlan_crypto_global_api.h"
#include "wlan_crypto_def_i.h"
#include "wlan_crypto_param_handling_i.h"
#include "wlan_crypto_obj_mgr_i.h"
#include <qdf_module.h>
const struct wlan_crypto_cipher *wlan_crypto_cipher_ops[WLAN_CRYPTO_CIPHER_MAX];
/**
* wlan_crypto_vdev_get_crypto_params - called by mlme to get crypto params
*
* @vdev:vdev
*
* This function gets called by mlme to get crypto params
*
* Return: wlan_crypto_params or NULL in case of failure
*/
static struct wlan_crypto_params *wlan_crypto_vdev_get_comp_params(
struct wlan_objmgr_vdev *vdev,
struct wlan_crypto_comp_priv **crypto_priv){
*crypto_priv = (struct wlan_crypto_comp_priv *)
wlan_get_vdev_crypto_obj(vdev);
if (*crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return NULL;
}
return &((*crypto_priv)->crypto_params);
}
/**
* wlan_crypto_peer_get_crypto_params - called by mlme to get crypto params
*
* @peer:peer
*
* This function gets called by mlme to get crypto params
*
* Return: wlan_crypto_params or NULL in case of failure
*/
static struct wlan_crypto_params *wlan_crypto_peer_get_comp_params(
struct wlan_objmgr_peer *peer,
struct wlan_crypto_comp_priv **crypto_priv){
*crypto_priv = (struct wlan_crypto_comp_priv *)
wlan_get_peer_crypto_obj(peer);
if (*crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return NULL;
}
return &((*crypto_priv)->crypto_params);
}
static QDF_STATUS wlan_crypto_set_igtk_key(struct wlan_crypto_key *key)
{
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_set_param - called by ucfg to set crypto param
*
* @vdev: vdev
* @param: param to be set.
* @value: value
*
* This function gets called from ucfg to set param
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_set_param(struct wlan_objmgr_vdev *vdev,
wlan_crypto_param_type param,
uint32_t value){
QDF_STATUS status = QDF_STATUS_E_INVAL;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
crypto_priv = (struct wlan_crypto_comp_priv *)
wlan_get_vdev_crypto_obj(vdev);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = &(crypto_priv->crypto_params);
switch (param) {
case WLAN_CRYPTO_PARAM_AUTH_MODE:
status = wlan_crypto_set_authmode(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_UCAST_CIPHER:
status = wlan_crypto_set_ucastciphers(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_MCAST_CIPHER:
status = wlan_crypto_set_mcastcipher(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_MGMT_CIPHER:
status = wlan_crypto_set_mgmtcipher(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_CIPHER_CAP:
status = wlan_crypto_set_cipher_cap(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_RSN_CAP:
status = wlan_crypto_set_rsn_cap(crypto_params, value);
break;
case WLAN_CRYPTO_PARAM_KEY_MGMT:
status = wlan_crypto_set_key_mgmt(crypto_params, value);
break;
default:
status = QDF_STATUS_E_INVAL;
}
return status;
}
/**
* wlan_crypto_get_param_value - called by crypto APIs to get value for param
*
* @param: Crypto param type
* @crypto_params: Crypto params struct
*
* This function gets called from in-within crypto layer
*
* Return: value or -1 for failure
*/
static int32_t wlan_crypto_get_param_value(wlan_crypto_param_type param,
struct wlan_crypto_params *crypto_params)
{
int32_t value = -1;
switch (param) {
case WLAN_CRYPTO_PARAM_AUTH_MODE:
value = wlan_crypto_get_authmode(crypto_params);
break;
case WLAN_CRYPTO_PARAM_UCAST_CIPHER:
value = wlan_crypto_get_ucastciphers(crypto_params);
break;
case WLAN_CRYPTO_PARAM_MCAST_CIPHER:
value = wlan_crypto_get_mcastcipher(crypto_params);
break;
case WLAN_CRYPTO_PARAM_MGMT_CIPHER:
value = wlan_crypto_get_mgmtciphers(crypto_params);
break;
case WLAN_CRYPTO_PARAM_CIPHER_CAP:
value = wlan_crypto_get_cipher_cap(crypto_params);
break;
case WLAN_CRYPTO_PARAM_RSN_CAP:
value = wlan_crypto_get_rsn_cap(crypto_params);
break;
case WLAN_CRYPTO_PARAM_KEY_MGMT:
value = wlan_crypto_get_key_mgmt(crypto_params);
break;
default:
value = QDF_STATUS_E_INVAL;
}
return value;
}
int32_t wlan_crypto_get_param(struct wlan_objmgr_vdev *vdev,
wlan_crypto_param_type param)
{
int32_t value = -1;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
crypto_priv = (struct wlan_crypto_comp_priv *)
wlan_get_vdev_crypto_obj(vdev);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = &(crypto_priv->crypto_params);
value = wlan_crypto_get_param_value(param, crypto_params);
return value;
}
int32_t wlan_crypto_get_peer_param(struct wlan_objmgr_peer *peer,
wlan_crypto_param_type param)
{
int32_t value = -1;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
if (crypto_params == NULL) {
qdf_print("%s[%d] crypto_params NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
value = wlan_crypto_get_param_value(param, crypto_params);
return value;
}
uint8_t wlan_crypto_is_htallowed(struct wlan_objmgr_vdev *vdev,
struct wlan_objmgr_peer *peer)
{
int32_t ucast_cipher;
if (!(vdev || peer)) {
qdf_print("%s[%d] Invalid params\n", __func__, __LINE__);
return 0;
}
if (vdev)
ucast_cipher = wlan_crypto_get_param(vdev,
WLAN_CRYPTO_PARAM_UCAST_CIPHER);
else
ucast_cipher = wlan_crypto_get_peer_param(peer,
WLAN_CRYPTO_PARAM_UCAST_CIPHER);
return (ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_WEP)) ||
((ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_TKIP)) &&
!(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_CCM)) &&
!(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM)) &&
!(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_GCM_256)) &&
!(ucast_cipher & (1 << WLAN_CRYPTO_CIPHER_AES_CCM_256)));
}
qdf_export_symbol(wlan_crypto_is_htallowed);
/**
* wlan_crypto_setkey - called by ucfg to setkey
*
* @vdev: vdev
* @req_key: req_key with cipher type, key macaddress
*
* This function gets called from ucfg to sey key
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_setkey(struct wlan_objmgr_vdev *vdev,
struct wlan_crypto_req_key *req_key){
QDF_STATUS status = QDF_STATUS_E_INVAL;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_objmgr_psoc *psoc;
struct wlan_objmgr_peer *peer;
struct wlan_crypto_key *key;
const struct wlan_crypto_cipher *cipher;
uint8_t macaddr[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
bool isbcast;
enum tQDF_ADAPTER_MODE vdev_mode;
if (!vdev || !req_key || req_key->keylen > (sizeof(req_key->keydata))) {
qdf_print("%s[%d] Invalid params vdev%p, req_key%p\n",
__func__, __LINE__, vdev, req_key);
return QDF_STATUS_E_INVAL;
}
isbcast = qdf_is_macaddr_broadcast(
(struct qdf_mac_addr *)req_key->macaddr);
if (req_key->keylen == 0) {
/* zero length keys, only set default key id if flags are set*/
if ((req_key->flags & WLAN_CRYPTO_KEY_DEFAULT)
&& (req_key->keyix != WLAN_CRYPTO_KEYIX_NONE)
&& (!IS_MGMT_CIPHER(req_key->type))) {
wlan_crypto_default_key(vdev,
req_key->macaddr,
req_key->keyix,
!isbcast);
return QDF_STATUS_SUCCESS;
}
qdf_print("%s[%d] req_key len zero\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
cipher = wlan_crypto_cipher_ops[req_key->type];
if (!cipher && !IS_MGMT_CIPHER(req_key->type)) {
qdf_print("%s[%d] cipher invalid\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (cipher && (!IS_MGMT_CIPHER(req_key->type))
&& ((req_key->keylen != (cipher->keylen/NBBY))
&& (req_key->type != WLAN_CRYPTO_CIPHER_WEP))) {
qdf_print("%s[%d] cipher invalid\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
} else if ((req_key->type == WLAN_CRYPTO_CIPHER_WEP) &&
!((req_key->keylen != WLAN_CRYPTO_KEY_WEP40_LEN)
|| (req_key->keylen != WLAN_CRYPTO_KEY_WEP104_LEN)
|| (req_key->keylen != WLAN_CRYPTO_KEY_WEP128_LEN))) {
qdf_print("%s[%d] cipher invalid\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE) {
if (req_key->flags != (WLAN_CRYPTO_KEY_XMIT
| WLAN_CRYPTO_KEY_RECV)) {
req_key->flags |= (WLAN_CRYPTO_KEY_XMIT
| WLAN_CRYPTO_KEY_RECV);
}
} else {
if ((req_key->keyix > WLAN_CRYPTO_MAXKEYIDX)
&& (!IS_MGMT_CIPHER(req_key->type))) {
return QDF_STATUS_E_INVAL;
}
req_key->flags |= (WLAN_CRYPTO_KEY_XMIT
| WLAN_CRYPTO_KEY_RECV);
if (isbcast)
req_key->flags |= WLAN_CRYPTO_KEY_GROUP;
}
vdev_mode = wlan_vdev_mlme_get_opmode(vdev);
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(macaddr, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (isbcast) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (IS_MGMT_CIPHER(req_key->type)) {
key = qdf_mem_malloc(sizeof(struct wlan_crypto_key));
if (key == NULL) {
qdf_print("%s[%d] igtk key alloc failed\n",
__func__, __LINE__);
return QDF_STATUS_E_NOMEM;
}
crypto_priv->igtk_key = key;
crypto_priv->igtk_key_type = req_key->type;
} else {
if (!HAS_MCAST_CIPHER(crypto_params, req_key->type)
&& (req_key->type != WLAN_CRYPTO_CIPHER_WEP)) {
return QDF_STATUS_E_INVAL;
}
if (!crypto_priv->key[req_key->keyix]) {
crypto_priv->key[req_key->keyix]
= qdf_mem_malloc(
sizeof(struct wlan_crypto_key));
if (!crypto_priv->key[req_key->keyix])
return QDF_STATUS_E_NOMEM;
}
key = crypto_priv->key[req_key->keyix];
}
if (vdev_mode == QDF_STA_MODE) {
peer = wlan_vdev_get_bsspeer(vdev);
if (!peer) {
qdf_print("%s[%d] peer is null\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_objmgr_peer_try_get_ref(peer, WLAN_CRYPTO_ID);
wlan_peer_obj_lock(peer);
qdf_mem_copy(macaddr, wlan_peer_get_macaddr(peer),
WLAN_ALEN);
wlan_peer_obj_unlock(peer);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
}
} else {
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc,
req_key->macaddr,
macaddr,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] peer NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
qdf_mem_copy(macaddr, req_key->macaddr, WLAN_ALEN);
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (IS_MGMT_CIPHER(req_key->type)) {
key = qdf_mem_malloc(sizeof(struct wlan_crypto_key));
if (key == NULL) {
qdf_print("%s[%d] igtk key alloc failed\n",
__func__, __LINE__);
return QDF_STATUS_E_NOMEM;
}
crypto_priv->igtk_key = key;
} else {
uint16_t kid = req_key->keyix;
if (kid == WLAN_CRYPTO_KEYIX_NONE)
kid = 0;
if (!crypto_priv->key[kid]) {
crypto_priv->key[kid]
= qdf_mem_malloc(
sizeof(struct wlan_crypto_key));
if (!crypto_priv->key[kid])
return QDF_STATUS_E_NOMEM;
}
key = crypto_priv->key[kid];
}
}
/* alloc key might not required as it is already there */
key->cipher_table = (void *)cipher;
key->keylen = req_key->keylen;
key->flags = req_key->flags;
if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE)
key->keyix = 0;
else
key->keyix = req_key->keyix;
if (req_key->flags & WLAN_CRYPTO_KEY_DEFAULT
&& (!IS_MGMT_CIPHER(req_key->type))) {
crypto_priv->def_tx_keyid = key->keyix;
key->flags |= WLAN_CRYPTO_KEY_DEFAULT;
}
if ((req_key->type == WLAN_CRYPTO_CIPHER_WAPI_SMS4)
|| (req_key->type == WLAN_CRYPTO_CIPHER_WAPI_GCM4)) {
uint8_t iv_AP[16] = { 0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x37};
uint8_t iv_STA[16] = { 0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x36,
0x5c, 0x36, 0x5c, 0x36};
/* During Tx PN should be increment and
* send but as per our implementation we increment only after
* Tx complete. So First packet PN check will be failed.
* To compensate increment the PN here by 2
*/
if (vdev_mode == QDF_SAP_MODE) {
iv_AP[15] += 2;
qdf_mem_copy(key->recviv, iv_STA,
WLAN_CRYPTO_WAPI_IV_SIZE);
qdf_mem_copy(key->txiv, iv_AP,
WLAN_CRYPTO_WAPI_IV_SIZE);
} else {
iv_STA[15] += 2;
qdf_mem_copy(key->recviv, iv_AP,
WLAN_CRYPTO_WAPI_IV_SIZE);
qdf_mem_copy(key->txiv, iv_STA,
WLAN_CRYPTO_WAPI_IV_SIZE);
}
} else {
uint8_t i = 0;
qdf_mem_copy((uint8_t *)(&key->keytsc),
(uint8_t *)(&req_key->keytsc), sizeof(key->keytsc));
for (i = 0; i < WLAN_CRYPTO_TID_SIZE; i++) {
qdf_mem_copy((uint8_t *)(&key->keyrsc[i]),
(uint8_t *)(&req_key->keyrsc),
sizeof(key->keyrsc[0]));
}
}
qdf_mem_copy(key->keyval, req_key->keydata, sizeof(key->keyval));
key->valid = 1;
if ((IS_MGMT_CIPHER(req_key->type))) {
if (HAS_CIPHER_CAP(crypto_params,
WLAN_CRYPTO_CAP_PMF_OFFLOAD)) {
if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev,
key, macaddr, req_key->type);
}
}
status = wlan_crypto_set_igtk_key(key);
return status;
} else {
if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev, key,
macaddr, req_key->type);
}
}
status = cipher->setkey(key);
return status;
}
/**
* wlan_crypto_getkey - called by ucfg to get key
*
* @vdev: vdev
* @req_key: key value will be copied in this req_key
* @mac_address: mac address of the peer for unicast key
* or broadcast address if group key is requested.
* This function gets called from ucfg to get key
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_getkey(struct wlan_objmgr_vdev *vdev,
struct wlan_crypto_req_key *req_key,
uint8_t *mac_addr){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_cipher *cipher_table;
struct wlan_crypto_key *key;
struct wlan_objmgr_psoc *psoc;
uint8_t macaddr[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if ((req_key->keyix != WLAN_CRYPTO_KEYIX_NONE) &&
(req_key->keyix >= WLAN_CRYPTO_MAXKEYIDX)) {
qdf_print("%s[%d] invalid keyix %d\n", __func__, __LINE__,
req_key->keyix);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(macaddr, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE)
key = crypto_priv->key[crypto_priv->def_tx_keyid];
else
key = crypto_priv->key[req_key->keyix];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc,
mac_addr,
macaddr,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] peer NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (req_key->keyix == WLAN_CRYPTO_KEYIX_NONE)
key = crypto_priv->key[crypto_priv->def_tx_keyid];
else
key = crypto_priv->key[req_key->keyix];
if (!key)
return QDF_STATUS_E_INVAL;
}
if (key->valid) {
qdf_mem_copy(req_key->keydata,
key->keyval, key->keylen);
qdf_mem_copy((uint8_t *)(&req_key->keytsc),
(uint8_t *)(&key->keytsc),
sizeof(req_key->keytsc));
qdf_mem_copy((uint8_t *)(&req_key->keyrsc),
(uint8_t *)(&key->keyrsc[0]),
sizeof(req_key->keyrsc));
req_key->keylen = key->keylen;
req_key->flags = key->flags;
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
if (!cipher_table)
return QDF_STATUS_SUCCESS;
req_key->type = cipher_table->cipher;
}
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_delkey - called by ucfg to delete key
*
* @vdev: vdev
* @mac_address: mac address of the peer for unicast key
* or broadcast address if group key is deleted.
* @key_idx: key index to be deleted
* This function gets called from ucfg to delete key
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_delkey(struct wlan_objmgr_vdev *vdev,
uint8_t *macaddr,
uint8_t key_idx){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if (!vdev || !macaddr || (key_idx >= WLAN_CRYPTO_MAXKEYIDX)) {
qdf_print("%s[%d] Invalid params vdev %p, macaddr %p"
"keyidx %d\n", __func__, __LINE__, vdev,
macaddr, key_idx);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)macaddr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[key_idx];
if (!key)
return QDF_STATUS_E_INVAL;
crypto_priv->key[key_idx] = NULL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, macaddr,
bssid_mac,
WLAN_CRYPTO_ID);
if (peer == NULL) {
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[key_idx];
if (!key)
return QDF_STATUS_E_INVAL;
crypto_priv->key[key_idx] = NULL;
}
if (key->valid) {
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
if (WLAN_CRYPTO_TX_OPS_DELKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_DELKEY(psoc)(vdev, key,
macaddr, cipher_table->cipher);
}
}
qdf_mem_free(key);
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_default_key - called by ucfg to set default tx key
*
* @vdev: vdev
* @mac_address: mac address of the peer for unicast key
* or broadcast address if group key need to made default.
* @key_idx: key index to be made as default key
* @unicast: is key was unicast or group key.
* This function gets called from ucfg to set default key
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_default_key(struct wlan_objmgr_vdev *vdev,
uint8_t *macaddr,
uint8_t key_idx,
bool unicast){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (!vdev || !macaddr || (key_idx >= WLAN_CRYPTO_MAXKEYIDX)) {
qdf_print("%s[%d] Invalid params vdev %p, macaddr %p"
"keyidx %d\n", __func__, __LINE__,
vdev, macaddr, key_idx);
return QDF_STATUS_E_INVAL;
}
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)macaddr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[key_idx];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, macaddr,
bssid_mac,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] peer NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[key_idx];
if (!key)
return QDF_STATUS_E_INVAL;
}
if (!key->valid)
return QDF_STATUS_E_INVAL;
if (WLAN_CRYPTO_TX_OPS_DEFAULTKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_DEFAULTKEY(psoc)(vdev, key_idx,
macaddr);
}
crypto_priv->def_tx_keyid = key_idx;
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_encap - called by mgmt for encap the frame based on cipher
*
* @vdev: vdev
* @wbuf: wbuf
* @macaddr: macaddr
* @encapdone: is encapdone already or not.
* This function gets called from mgmt txrx to encap frame.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_encap(struct wlan_objmgr_vdev *vdev,
qdf_nbuf_t wbuf,
uint8_t *mac_addr,
uint8_t encapdone){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
QDF_STATUS status;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if (!wlan_crypto_is_data_protected((uint8_t *)qdf_nbuf_data(wbuf)))
return QDF_STATUS_E_INVAL;
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, mac_addr, bssid_mac, WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
}
/* if tkip, is counter measures enabled, then drop the frame */
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
status = cipher_table->encap(key, wbuf, encapdone,
ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)));
return status;
}
EXPORT_SYMBOL(wlan_crypto_encap);
/**
* wlan_crypto_decap - called by mgmt for decap the frame based on cipher
*
* @vdev: vdev
* @wbuf: wbuf
* @macaddr: macaddr
* @tid: tid of the frame
* This function gets called from mgmt txrx to decap frame.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_decap(struct wlan_objmgr_vdev *vdev,
qdf_nbuf_t wbuf,
uint8_t *mac_addr,
uint8_t tid){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
QDF_STATUS status;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
if (!wlan_crypto_is_data_protected((uint8_t *)qdf_nbuf_data(wbuf)))
return QDF_STATUS_E_INVAL;
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, mac_addr, bssid_mac,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] peer NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
}
/* if tkip, is counter measures enabled, then drop the frame */
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
status = cipher_table->decap(key, wbuf, tid,
ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)));
return status;
}
EXPORT_SYMBOL(wlan_crypto_decap);
/**
* wlan_crypto_enmic - called by mgmt for adding mic in frame based on cipher
*
* @vdev: vdev
* @wbuf: wbuf
* @macaddr: macaddr
* @encapdone: is encapdone already or not.
* This function gets called from mgmt txrx to adding mic to the frame.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_enmic(struct wlan_objmgr_vdev *vdev,
qdf_nbuf_t wbuf,
uint8_t *mac_addr,
uint8_t encapdone){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
QDF_STATUS status;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, mac_addr, bssid_mac,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
}
/* if tkip, is counter measures enabled, then drop the frame */
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
status = cipher_table->enmic(key, wbuf, encapdone,
ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)));
return status;
}
/**
* wlan_crypto_demic - called by mgmt for remove and check mic for
* the frame based on cipher
*
* @vdev: vdev
* @wbuf: wbuf
* @macaddr: macaddr
* @tid: tid of the frame
* This function gets called from mgmt txrx to decap frame.
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_demic(struct wlan_objmgr_vdev *vdev,
qdf_nbuf_t wbuf,
uint8_t *mac_addr,
uint8_t tid){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
QDF_STATUS status;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
uint8_t bssid_mac[WLAN_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
wlan_vdev_obj_lock(vdev);
qdf_mem_copy(bssid_mac, wlan_vdev_mlme_get_macaddr(vdev), WLAN_ALEN);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
wlan_vdev_obj_unlock(vdev);
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
wlan_vdev_obj_unlock(vdev);
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr)) {
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
} else {
struct wlan_objmgr_peer *peer;
peer = wlan_objmgr_get_peer_by_mac_n_vdev(
psoc, mac_addr, bssid_mac,
WLAN_CRYPTO_ID);
if (peer == NULL) {
qdf_print("%s[%d] peer NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
wlan_objmgr_peer_release_ref(peer, WLAN_CRYPTO_ID);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n",
__func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
key = crypto_priv->key[crypto_priv->def_tx_keyid];
if (!key)
return QDF_STATUS_E_INVAL;
}
/* if tkip, is counter measures enabled, then drop the frame */
cipher_table = (struct wlan_crypto_cipher *)key->cipher_table;
status = cipher_table->demic(key, wbuf, tid,
ieee80211_hdrsize((uint8_t *)qdf_nbuf_data(wbuf)));
return status;
}
/**
* wlan_crypto_vdev_is_pmf_enabled - called to check is pmf enabled in vdev
*
* @vdev: vdev
*
* This function gets called to check is pmf enabled or not in vdev.
*
* Return: true or false
*/
bool wlan_crypto_vdev_is_pmf_enabled(struct wlan_objmgr_vdev *vdev)
{
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *vdev_crypto_params;
if (!vdev)
return false;
vdev_crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if ((vdev_crypto_params->rsn_caps &
WLAN_CRYPTO_RSN_CAP_MFP_ENABLED)
|| (vdev_crypto_params->rsn_caps &
WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)) {
return true;
}
return false;
}
/**
* wlan_crypto_is_pmf_enabled - called by mgmt txrx to check is pmf enabled
*
* @vdev: vdev
* @peer: peer
*
* This function gets called by mgmt txrx to check is pmf enabled or not.
*
* Return: true or false
*/
bool wlan_crypto_is_pmf_enabled(struct wlan_objmgr_vdev *vdev,
struct wlan_objmgr_peer *peer){
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *vdev_crypto_params;
struct wlan_crypto_params *peer_crypto_params;
if (!vdev || !peer)
return false;
vdev_crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
peer_crypto_params = wlan_crypto_peer_get_comp_params(peer,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_INVAL;
}
if (((vdev_crypto_params->rsn_caps &
WLAN_CRYPTO_RSN_CAP_MFP_ENABLED) &&
(peer_crypto_params->rsn_caps &
WLAN_CRYPTO_RSN_CAP_MFP_ENABLED))
|| (vdev_crypto_params->rsn_caps &
WLAN_CRYPTO_RSN_CAP_MFP_REQUIRED)) {
return true;
}
return false;
}
/**
* wlan_crypto_add_mmie - called by mgmt txrx to add mmie in frame
*
* @vdev: vdev
* @bfrm: frame starting pointer
* @len: length of the frame
*
* This function gets called by mgmt txrx to add mmie in frame
*
* Return: end of frame or NULL in case failure
*/
uint8_t *wlan_crypto_add_mmie(struct wlan_objmgr_vdev *vdev,
uint8_t *bfrm,
uint32_t len) {
struct wlan_crypto_key *key;
struct wlan_crypto_mmie *mmie;
uint8_t *pn, *aad, *buf, *efrm, nounce[12];
struct ieee80211_hdr *hdr;
uint32_t i, hdrlen, mic_len, aad_len;
uint8_t mic[16];
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
int32_t ret;
if (!bfrm) {
qdf_print("%s[%d] frame is NULL\n", __func__, __LINE__);
return NULL;
}
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return NULL;
}
key = crypto_priv->igtk_key;
if (!key) {
qdf_print("%s[%d] No igtk key present\n", __func__, __LINE__);
return NULL;
}
mic_len = (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16;
efrm = bfrm + len;
aad_len = 20;
hdrlen = sizeof(struct ieee80211_hdr);
len += sizeof(struct wlan_crypto_mmie);
mmie = (struct wlan_crypto_mmie *) efrm;
qdf_mem_zero((unsigned char *)mmie, sizeof(*mmie));
mmie->element_id = WLAN_ELEMID_MMIE;
mmie->length = sizeof(*mmie) - 2;
mmie->key_id = qdf_cpu_to_le16(key->keyix);
mic_len = (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16;
if (mic_len == 8) {
mmie->length -= 8;
len -= 8;
}
/* PN = PN + 1 */
pn = (uint8_t *)&key->keytsc;
for (i = 0; i <= 5; i++) {
pn[i]++;
if (pn[i])
break;
}
/* Copy IPN */
qdf_mem_copy(mmie->sequence_number, pn, 6);
hdr = (struct ieee80211_hdr *) bfrm;
buf = qdf_mem_malloc(len - hdrlen + 20);
if (!buf) {
qdf_print("%s[%d] malloc failed\n", __func__, __LINE__);
return NULL;
}
qdf_mem_zero(buf, len - hdrlen + 20);
aad = buf;
/* generate BIP AAD: FC(masked) || A1 || A2 || A3 */
/* FC type/subtype */
aad[0] = hdr->frame_control & 0xff;
/* Mask FC Retry, PwrMgt, MoreData flags to zero */
aad[1] = (hdr->frame_control & ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT
| WLAN_FC_MOREDATA)) >> 8;
/* A1 || A2 || A3 */
qdf_mem_copy(aad + 2, hdr->addr1, WLAN_ALEN);
qdf_mem_copy(aad + 8, hdr->addr2, WLAN_ALEN);
qdf_mem_copy(aad + 14, hdr->addr3, WLAN_ALEN);
qdf_mem_zero(mic, 16);
/*
* MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)
*/
qdf_mem_copy(buf + aad_len, bfrm + hdrlen, len - hdrlen);
if (crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_CMAC) {
ret = omac1_aes_128(key->keyval, buf,
len + aad_len - hdrlen, mic);
qdf_mem_copy(mmie->mic, mic, 8);
} else if (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_CMAC_256) {
ret = omac1_aes_256(key->keyval, buf,
len + aad_len - hdrlen, mmie->mic);
} else if ((crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_GMAC)
|| (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_GMAC_256)) {
qdf_mem_copy(nounce, hdr->addr2, WLAN_ALEN);
qdf_mem_copy(nounce + 6, pn, 6);
ret = wlan_crypto_aes_gmac(key->keyval, key->keylen, nounce,
sizeof(nounce), buf,
len + aad_len - hdrlen, mmie->mic);
}
qdf_mem_free(buf);
if (ret < 0) {
qdf_print("%s[%d] add mmie failed\n", __func__, __LINE__);
return NULL;
}
key->keytsc++;
return bfrm + len;
}
/**
* wlan_crypto_is_mmie_valid - called by mgmt txrx to check mmie of the frame
*
* @vdev: vdev
* @frm: frame starting pointer
* @efrm: end of frame pointer
*
* This function gets called by mgmt txrx to check mmie of the frame
*
* Return: true or false
*/
bool wlan_crypto_is_mmie_valid(struct wlan_objmgr_vdev *vdev,
uint8_t *frm,
uint8_t *efrm){
struct wlan_crypto_mmie *mmie = NULL;
uint8_t *ipn, *aad, *buf, mic[16], nounce[12];
struct wlan_crypto_key *key;
struct ieee80211_hdr *hdr;
uint16_t mic_len, hdrlen, len;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
uint8_t aad_len = 20;
int32_t ret;
/* check if frame is illegal length */
if (!frm || !efrm || (efrm < frm)
|| ((efrm - frm) < sizeof(struct ieee80211_hdr))) {
qdf_print("%s[%d] Invalid params\n", __func__, __LINE__);
return false;
}
len = efrm - frm;
crypto_priv = (struct wlan_crypto_comp_priv *)
wlan_get_vdev_crypto_obj(vdev);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return false;
}
crypto_params = &(crypto_priv->crypto_params);
key = crypto_priv->igtk_key;
if (!key) {
qdf_print("%s[%d] No igtk key present\n", __func__, __LINE__);
return false;
}
mic_len = (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_CMAC) ? 8 : 16;
hdrlen = sizeof(struct ieee80211_hdr);
if (mic_len == 8)
mmie = (struct wlan_crypto_mmie *)(efrm - sizeof(*mmie) + 8);
else
mmie = (struct wlan_crypto_mmie *)(efrm - sizeof(*mmie));
/* check Elem ID*/
if ((mmie == NULL) || (mmie->element_id != WLAN_ELEMID_MMIE)) {
qdf_print("%s[%d] IE is not MMIE\n", __func__, __LINE__);
return false;
}
/* validate ipn */
ipn = mmie->sequence_number;
if (qdf_mem_cmp(ipn, key->keyrsc, 6) <= 0) {
qdf_print("%s[%d] replay error\n", __func__, __LINE__);
return false;
}
buf = qdf_mem_malloc(len - hdrlen + 20);
if (!buf) {
qdf_print("%s[%d] malloc failed\n", __func__, __LINE__);
return false;
}
aad = buf;
/* construct AAD */
hdr = (struct ieee80211_hdr *)frm;
/* generate BIP AAD: FC(masked) || A1 || A2 || A3 */
/* FC type/subtype */
aad[0] = hdr->frame_control & 0xff;
/* Mask FC Retry, PwrMgt, MoreData flags to zero */
aad[1] = (hdr->frame_control & ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT
| WLAN_FC_MOREDATA)) >> 8;
/* A1 || A2 || A3 */
qdf_mem_copy(aad + 2, hdr->addr1, 3 * WLAN_ALEN);
/*
* MIC = AES-128-CMAC(IGTK, AAD || Management Frame Body || MMIE, 64)
*/
qdf_mem_copy(buf + 20, frm + hdrlen, len - hdrlen);
qdf_mem_zero(buf + (len - hdrlen + 20 - mic_len), mic_len);
qdf_mem_zero(mic, 16);
if (crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_CMAC) {
ret = omac1_aes_128(key->keyval, buf,
len - hdrlen + aad_len, mic);
} else if (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_CMAC_256) {
ret = omac1_aes_256(key->keyval, buf,
len + aad_len - hdrlen, mic);
} else if ((crypto_priv->igtk_key_type == WLAN_CRYPTO_CIPHER_AES_GMAC)
|| (crypto_priv->igtk_key_type
== WLAN_CRYPTO_CIPHER_AES_GMAC_256)) {
qdf_mem_copy(nounce, hdr->addr2, WLAN_ALEN);
qdf_mem_copy(nounce + 6, ipn, 6);
ret = wlan_crypto_aes_gmac(key->keyval, key->keylen, nounce,
sizeof(nounce), buf,
len + aad_len - hdrlen, mic);
}
qdf_mem_free(buf);
if (ret < 0) {
qdf_print("%s[%d] genarate mmie failed\n", __func__, __LINE__);
return false;
}
if (qdf_mem_cmp(mic, mmie->mic, mic_len) != 0) {
qdf_print("%s[%d] mmie mismatch\n", __func__, __LINE__);
/* MMIE MIC mismatch */
return false;
}
/* Update the receive sequence number */
qdf_mem_copy(key->keyrsc, ipn, 6);
qdf_print("%s[%d] mmie matched\n", __func__, __LINE__);
return true;
}
static int32_t wlan_crypto_wpa_cipher_to_suite(uint32_t cipher)
{
int32_t status = -1;
switch (cipher) {
case WLAN_CRYPTO_CIPHER_TKIP:
return WPA_CIPHER_SUITE_TKIP;
case WLAN_CRYPTO_CIPHER_AES_CCM:
return WPA_CIPHER_SUITE_CCMP;
case WLAN_CRYPTO_CIPHER_NONE:
return WPA_CIPHER_SUITE_NONE;
}
return status;
}
static int32_t wlan_crypto_rsn_cipher_to_suite(uint32_t cipher)
{
int32_t status = -1;
switch (cipher) {
case WLAN_CRYPTO_CIPHER_TKIP:
return RSN_CIPHER_SUITE_TKIP;
case WLAN_CRYPTO_CIPHER_AES_CCM:
return RSN_CIPHER_SUITE_CCMP;
case WLAN_CRYPTO_CIPHER_AES_CCM_256:
return RSN_CIPHER_SUITE_CCMP_256;
case WLAN_CRYPTO_CIPHER_AES_GCM:
return RSN_CIPHER_SUITE_GCMP;
case WLAN_CRYPTO_CIPHER_AES_GCM_256:
return RSN_CIPHER_SUITE_GCMP_256;
case WLAN_CRYPTO_CIPHER_AES_CMAC:
return RSN_CIPHER_SUITE_AES_CMAC;
case WLAN_CRYPTO_CIPHER_AES_CMAC_256:
return RSN_CIPHER_SUITE_BIP_CMAC_256;
case WLAN_CRYPTO_CIPHER_AES_GMAC:
return RSN_CIPHER_SUITE_BIP_GMAC_128;
case WLAN_CRYPTO_CIPHER_AES_GMAC_256:
return RSN_CIPHER_SUITE_BIP_GMAC_256;
case WLAN_CRYPTO_CIPHER_NONE:
return RSN_CIPHER_SUITE_NONE;
}
return status;
}
/*
* Convert an RSN key management/authentication algorithm
* to an internal code.
*/
static int32_t
wlan_crypto_rsn_keymgmt_to_suite(uint32_t keymgmt)
{
int32_t status = -1;
switch (keymgmt) {
case WLAN_CRYPTO_KEY_MGMT_NONE:
return RSN_AUTH_KEY_MGMT_NONE;
case WLAN_CRYPTO_KEY_MGMT_IEEE8021X:
return RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
case WLAN_CRYPTO_KEY_MGMT_PSK:
return RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X;
case WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X:
return RSN_AUTH_KEY_MGMT_FT_802_1X;
case WLAN_CRYPTO_KEY_MGMT_FT_PSK:
return RSN_AUTH_KEY_MGMT_FT_PSK;
case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256:
return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
case WLAN_CRYPTO_KEY_MGMT_PSK_SHA256:
return RSN_AUTH_KEY_MGMT_PSK_SHA256;
case WLAN_CRYPTO_KEY_MGMT_SAE:
return RSN_AUTH_KEY_MGMT_SAE;
case WLAN_CRYPTO_KEY_MGMT_FT_SAE:
return RSN_AUTH_KEY_MGMT_FT_SAE;
case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B:
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
case WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192:
return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
case WLAN_CRYPTO_KEY_MGMT_CCKM:
return RSN_AUTH_KEY_MGMT_CCKM;
case WLAN_CRYPTO_KEY_MGMT_OSEN:
return RSN_AUTH_KEY_MGMT_OSEN;
}
return status;
}
/*
* Convert an RSN key management/authentication algorithm
* to an internal code.
*/
static int32_t
wlan_crypto_wpa_keymgmt_to_suite(uint32_t keymgmt)
{
int32_t status = -1;
switch (keymgmt) {
case WLAN_CRYPTO_KEY_MGMT_NONE:
return WPA_AUTH_KEY_MGMT_NONE;
case WLAN_CRYPTO_KEY_MGMT_IEEE8021X:
return WPA_AUTH_KEY_MGMT_UNSPEC_802_1X;
case WLAN_CRYPTO_KEY_MGMT_PSK:
return WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X;
case WLAN_CRYPTO_KEY_MGMT_CCKM:
return WPA_AUTH_KEY_MGMT_CCKM;
}
return status;
}
/**
*
* Convert a WPA cipher selector OUI to an internal
* cipher algorithm. Where appropriate we also
* record any key length.
*/
static int32_t wlan_crypto_wpa_suite_to_cipher(uint8_t *sel)
{
uint32_t w = BE_READ_4(sel);
int32_t status = -1;
switch (w) {
case WPA_CIPHER_SUITE_TKIP:
return WLAN_CRYPTO_CIPHER_TKIP;
case WPA_CIPHER_SUITE_CCMP:
return WLAN_CRYPTO_CIPHER_AES_CCM;
case WPA_CIPHER_SUITE_NONE:
return WLAN_CRYPTO_CIPHER_NONE;
}
return status;
}
/*
* Convert a WPA key management/authentication algorithm
* to an internal code.
*/
static int32_t wlan_crypto_wpa_suite_to_keymgmt(uint8_t *sel)
{
uint32_t w = BE_READ_4(sel);
int32_t status = -1;
switch (w) {
case WPA_AUTH_KEY_MGMT_UNSPEC_802_1X:
return WLAN_CRYPTO_KEY_MGMT_IEEE8021X;
case WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X:
return WLAN_CRYPTO_KEY_MGMT_PSK;
case WPA_AUTH_KEY_MGMT_CCKM:
return WLAN_CRYPTO_KEY_MGMT_CCKM;
case WPA_AUTH_KEY_MGMT_NONE:
return WLAN_CRYPTO_KEY_MGMT_NONE;
}
return status;
}
/*
* Convert a RSN cipher selector OUI to an internal
* cipher algorithm. Where appropriate we also
* record any key length.
*/
static int32_t wlan_crypto_rsn_suite_to_cipher(uint8_t *sel)
{
uint32_t w = BE_READ_4(sel);
int32_t status = -1;
switch (w) {
case RSN_CIPHER_SUITE_TKIP:
return WLAN_CRYPTO_CIPHER_TKIP;
case RSN_CIPHER_SUITE_CCMP:
return WLAN_CRYPTO_CIPHER_AES_CCM;
case RSN_CIPHER_SUITE_CCMP_256:
return WLAN_CRYPTO_CIPHER_AES_CCM_256;
case RSN_CIPHER_SUITE_GCMP:
return WLAN_CRYPTO_CIPHER_AES_GCM;
case RSN_CIPHER_SUITE_GCMP_256:
return WLAN_CRYPTO_CIPHER_AES_GCM_256;
case RSN_CIPHER_SUITE_AES_CMAC:
return WLAN_CRYPTO_CIPHER_AES_CMAC;
case RSN_CIPHER_SUITE_BIP_CMAC_256:
return WLAN_CRYPTO_CIPHER_AES_CMAC_256;
case RSN_CIPHER_SUITE_BIP_GMAC_128:
return WLAN_CRYPTO_CIPHER_AES_GMAC;
case RSN_CIPHER_SUITE_BIP_GMAC_256:
return WLAN_CRYPTO_CIPHER_AES_GMAC_256;
case RSN_CIPHER_SUITE_NONE:
return WLAN_CRYPTO_CIPHER_NONE;
}
return status;
}
/*
* Convert an RSN key management/authentication algorithm
* to an internal code.
*/
static int32_t wlan_crypto_rsn_suite_to_keymgmt(uint8_t *sel)
{
uint32_t w = BE_READ_4(sel);
int32_t status = -1;
switch (w) {
case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
return WLAN_CRYPTO_KEY_MGMT_IEEE8021X;
case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
return WLAN_CRYPTO_KEY_MGMT_PSK;
case RSN_AUTH_KEY_MGMT_FT_802_1X:
return WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X;
case RSN_AUTH_KEY_MGMT_FT_PSK:
return WLAN_CRYPTO_KEY_MGMT_FT_PSK;
case RSN_AUTH_KEY_MGMT_802_1X_SHA256:
return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256;
case RSN_AUTH_KEY_MGMT_PSK_SHA256:
return WLAN_CRYPTO_KEY_MGMT_PSK_SHA256;
case RSN_AUTH_KEY_MGMT_SAE:
return WLAN_CRYPTO_KEY_MGMT_SAE;
case RSN_AUTH_KEY_MGMT_FT_SAE:
return WLAN_CRYPTO_KEY_MGMT_FT_SAE;
case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B;
case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
return WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SUITE_B_192;
case RSN_AUTH_KEY_MGMT_CCKM:
return WLAN_CRYPTO_KEY_MGMT_CCKM;
case RSN_AUTH_KEY_MGMT_OSEN:
return WLAN_CRYPTO_KEY_MGMT_OSEN;
}
return status;
}
/**
* wlan_crypto_wpaie_check - called by mlme to check the wpaie
*
*
* @crypto params: crypto params
* @iebuf: ie buffer
*
* This function gets called by mlme to check the contents of wpa is
* matching with given crypto params
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_wpaie_check(struct wlan_crypto_params *crypto_params,
uint8_t *frm){
uint8_t len = frm[1];
int32_t w;
int n;
/*
* Check the length once for fixed parts: OUI, type,
* version, mcast cipher, and 2 selector counts.
* Other, variable-length data, must be checked separately.
*/
RESET_AUTHMODE(crypto_params);
SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_WPA);
if (len < 14)
return QDF_STATUS_E_INVAL;
frm += 6, len -= 4;
w = LE_READ_2(frm);
if (w != WPA_VERSION)
return QDF_STATUS_E_INVAL;
frm += 2, len -= 2;
/* multicast/group cipher */
RESET_MCAST_CIPHERS(crypto_params);
w = wlan_crypto_wpa_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_MCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
/* unicast ciphers */
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4+2)
return QDF_STATUS_E_INVAL;
RESET_UCAST_CIPHERS(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_wpa_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_UCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
}
if (!crypto_params->ucastcipherset)
return QDF_STATUS_E_INVAL;
/* key management algorithms */
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4)
return QDF_STATUS_E_INVAL;
w = 0;
RESET_KEY_MGMT(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_wpa_suite_to_keymgmt(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_KEY_MGMT(crypto_params, w);
frm += 4, len -= 4;
}
/* optional capabilities */
if (len >= 2) {
crypto_params->rsn_caps = LE_READ_2(frm);
frm += 2, len -= 2;
}
return 0;
}
/**
* wlan_crypto_rsnie_check - called by mlme to check the rsnie
*
*
* @crypto params: crypto params
* @iebuf: ie buffer
*
* This function gets called by mlme to check the contents of wpa is
* matching with given crypto params
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_rsnie_check(struct wlan_crypto_params *crypto_params,
uint8_t *frm){
uint8_t len = frm[1];
int32_t w;
int n;
/*
* Check the length once for fixed parts: OUI, type,
* version, mcast cipher, and 2 selector counts.
* Other, variable-length data, must be checked separately.
*/
RESET_AUTHMODE(crypto_params);
SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_RSNA);
if (len < 14)
return QDF_STATUS_E_INVAL;
frm += 2, len -= 2;
/* NB: iswapoui already validated the OUI and type */
w = LE_READ_2(frm);
if (w != RSN_VERSION)
return QDF_STATUS_E_INVAL;
frm += 2, len -= 2;
/* multicast/group cipher */
RESET_MCAST_CIPHERS(crypto_params);
w = wlan_crypto_rsn_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_MCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
/* unicast ciphers */
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4+2)
return QDF_STATUS_E_INVAL;
RESET_UCAST_CIPHERS(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_rsn_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_UCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
}
if (crypto_params->ucastcipherset == 0)
return QDF_STATUS_E_INVAL;
/* key management algorithms */
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4)
return QDF_STATUS_E_INVAL;
w = 0;
RESET_KEY_MGMT(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_rsn_suite_to_keymgmt(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_KEY_MGMT(crypto_params, (1 << w));
frm += 4, len -= 4;
}
/* optional capabilities */
if (len >= 2) {
crypto_params->rsn_caps = LE_READ_2(frm);
frm += 2, len -= 2;
}
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_build_wpaie - called by mlme to build wpaie
*
* @crypto params: crypto params
* @iebuf: ie buffer
*
* This function gets called by mlme to build wpaie from given crypto params
*
* Return: end of buffer
*/
uint8_t *wlan_crypto_build_wpaie(struct wlan_crypto_params *crypto_params,
uint8_t *iebuf){
uint8_t *frm = iebuf;
uint8_t *selcnt;
uint32_t mcastcipher;
*frm++ = WLAN_ELEMID_VENDOR;
*frm++ = 0;
WLAN_CRYPTO_ADDSELECTOR(frm, WPA_TYPE_OUI);
WLAN_CRYPTO_ADDSHORT(frm, WPA_VERSION);
/* multicast cipher */
mcastcipher = wlan_crypto_get_mcastcipher(crypto_params);
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_cipher_to_suite(mcastcipher));
/* unicast cipher list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
/* do not use CCMP unicast cipher in WPA mode */
if (UCIPHER_IS_TKIP(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_cipher_to_suite(
WLAN_CRYPTO_CIPHER_TKIP));
}
if (UCIPHER_IS_CCMP128(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CCM));
}
if (UCIPHER_IS_CLEAR(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CCM));
}
/* authenticator selector list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_IEEE8021X)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_IEEE8021X));
} else if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_PSK)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_PSK));
} else if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_CCKM)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_CCKM));
} else {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_wpa_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_NONE));
}
/* calculate element length */
iebuf[1] = frm - iebuf - 2;
return frm;
}
/**
* wlan_crypto_build_rsnie - called by mlme to build rsnie
*
* @crypto params: crypto params
* @iebuf: ie buffer
*
* This function gets called by mlme to build rsnie from given crypto params
*
* Return: end of buffer
*/
uint8_t *wlan_crypto_build_rsnie(struct wlan_crypto_params *crypto_params,
uint8_t *iebuf){
uint8_t *frm = iebuf;
uint8_t *selcnt;
uint32_t mcastcipher;
*frm++ = WLAN_ELEMID_RSN;
*frm++ = 0;
WLAN_CRYPTO_ADDSHORT(frm, RSN_VERSION);
/* multicast cipher */
mcastcipher = wlan_crypto_get_mcastcipher(crypto_params);
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(mcastcipher));
/* unicast cipher list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
/* do not use CCMP unicast cipher in WPA mode */
if (UCIPHER_IS_TKIP(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_TKIP));
}
if (UCIPHER_IS_CCMP128(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CCM));
}
if (UCIPHER_IS_CCMP256(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CCM_256));
}
if (UCIPHER_IS_GCMP128(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_GCM));
}
if (UCIPHER_IS_GCMP256(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_GCM_256));
}
if (UCIPHER_IS_CLEAR(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CCM));
}
/* authenticator selector list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_CCKM)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_CCKM));
} else {
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_PSK)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_PSK));
}
if (HAS_KEY_MGMT(crypto_params,
WLAN_CRYPTO_KEY_MGMT_IEEE8021X)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_IEEE8021X));
}
if (HAS_KEY_MGMT(crypto_params,
WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_FT_IEEE8021X));
}
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_FT_PSK)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_FT_PSK));
}
if (HAS_KEY_MGMT(crypto_params,
WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_IEEE8021X_SHA256));
}
if (HAS_KEY_MGMT(crypto_params,
WLAN_CRYPTO_KEY_MGMT_PSK_SHA256)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_PSK_SHA256));
}
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_OSEN)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_keymgmt_to_suite(
WLAN_CRYPTO_KEY_MGMT_OSEN));
}
}
/* optional capabilities */
if (crypto_params->rsn_caps != 0 &&
crypto_params->rsn_caps != WLAN_CRYPTO_RSN_CAP_PREAUTH){
WLAN_CRYPTO_ADDSHORT(frm, crypto_params->rsn_caps);
if (HAS_MGMT_CIPHER(crypto_params,
WLAN_CRYPTO_CIPHER_AES_CMAC)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CMAC));
}
if (HAS_MGMT_CIPHER(crypto_params,
WLAN_CRYPTO_CIPHER_AES_GMAC)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_GMAC));
}
if (HAS_MGMT_CIPHER(crypto_params,
WLAN_CRYPTO_CIPHER_AES_CMAC_256)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_CMAC_256));
}
if (HAS_MGMT_CIPHER(crypto_params,
WLAN_CRYPTO_CIPHER_AES_GMAC_256)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
wlan_crypto_rsn_cipher_to_suite(
WLAN_CRYPTO_CIPHER_AES_GMAC_256));
}
}
/* calculate element length */
iebuf[1] = frm - iebuf - 2;
return frm;
}
bool wlan_crypto_rsn_info(struct wlan_objmgr_vdev *vdev,
struct wlan_crypto_params *crypto_params){
struct wlan_crypto_params *my_crypto_params;
my_crypto_params = wlan_crypto_vdev_get_crypto_params(vdev);
/*
* Check peer's pairwise ciphers.
* At least one must match with our unicast cipher
*/
if (!UCAST_CIPHER_MATCH(crypto_params, my_crypto_params))
return false;
/*
* Check peer's group cipher is our enabled multicast cipher.
*/
if (!MCAST_CIPHER_MATCH(crypto_params, my_crypto_params))
return false;
/*
* Check peer's key management class set (PSK or UNSPEC)
*/
if (!KEY_MGMTSET_MATCH(crypto_params, my_crypto_params))
return false;
return true;
}
/*
* Convert an WAPI CIPHER suite to to an internal code.
*/
static int32_t wlan_crypto_wapi_suite_to_cipher(uint8_t *sel)
{
uint32_t w = LE_READ_4(sel);
int32_t status = -1;
switch (w) {
case (WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER)):
return WLAN_CRYPTO_CIPHER_WAPI_SMS4;
}
return status;
}
/*
* Convert an WAPI key management/authentication algorithm
* to an internal code.
*/
static int32_t wlan_crypto_wapi_keymgmt(u_int8_t *sel)
{
uint32_t w = LE_READ_4(sel);
int32_t status = -1;
switch (w) {
case (WLAN_WAPI_SEL(WLAN_WAI_PSK)):
return WLAN_CRYPTO_KEY_MGMT_WAPI_PSK;
case (WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4)):
return WLAN_CRYPTO_KEY_MGMT_WAPI_CERT;
}
return status;
}
/**
* wlan_crypto_wapiie_check - called by mlme to check the wapiie
*
*
* @crypto params: crypto params
* @iebuf: ie buffer
*
* This function gets called by mlme to check the contents of wapi is
* matching with given crypto params
*
* Return: QDF_STATUS_SUCCESS - in case of success
*/
QDF_STATUS wlan_crypto_wapiie_check(struct wlan_crypto_params *crypto_params,
uint8_t *frm)
{
uint8_t len = frm[1];
int32_t w;
int n;
/*
* Check the length once for fixed parts: OUI, type,
* version, mcast cipher, and 2 selector counts.
* Other, variable-length data, must be checked separately.
*/
RESET_AUTHMODE(crypto_params);
SET_AUTHMODE(crypto_params, WLAN_CRYPTO_AUTH_WAPI);
if (len < WLAN_CRYPTO_WAPI_IE_LEN)
return QDF_STATUS_E_INVAL;
frm += 2;
w = LE_READ_2(frm);
frm += 2, len -= 2;
if (w != WAPI_VERSION)
return QDF_STATUS_E_INVAL;
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4+2)
return QDF_STATUS_E_INVAL;
RESET_KEY_MGMT(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_wapi_keymgmt(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_KEY_MGMT(crypto_params, w);
frm += 4, len -= 4;
}
/* unicast ciphers */
n = LE_READ_2(frm);
frm += 2, len -= 2;
if (len < n*4+2)
return QDF_STATUS_E_INVAL;
RESET_UCAST_CIPHERS(crypto_params);
for (; n > 0; n--) {
w = wlan_crypto_wapi_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_UCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
}
if (!crypto_params->ucastcipherset)
return QDF_STATUS_E_INVAL;
/* multicast/group cipher */
RESET_MCAST_CIPHERS(crypto_params);
w = wlan_crypto_wapi_suite_to_cipher(frm);
if (w < 0)
return QDF_STATUS_E_INVAL;
SET_MCAST_CIPHER(crypto_params, w);
frm += 4, len -= 4;
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_build_wapiie - called by mlme to build wapi ie
*
* @vdev: vdev
* @iebuf: ie buffer
*
* This function gets called by mlme to build wapi ie from given vdev
*
* Return: end of buffer
*/
uint8_t *wlan_crypto_build_wapiie(struct wlan_objmgr_vdev *vdev,
uint8_t *iebuf)
{
uint8_t *frm;
uint8_t *selcnt;
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
frm = iebuf;
if (!frm) {
qdf_print("%s[%d] ie buffer NULL\n", __func__, __LINE__);
return NULL;
}
crypto_params = wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv);
if (!crypto_params) {
qdf_print("%s[%d] crypto_params NULL\n", __func__, __LINE__);
return NULL;
}
*frm++ = WLAN_ELEMID_WAPI;
*frm++ = 0;
WLAN_CRYPTO_ADDSHORT(frm, WAPI_VERSION);
/* authenticator selector list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_WAPI_PSK)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
WLAN_WAPI_SEL(WLAN_WAI_PSK));
}
if (HAS_KEY_MGMT(crypto_params, WLAN_CRYPTO_KEY_MGMT_WAPI_CERT)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
WLAN_WAPI_SEL(WLAN_WAI_CERT_OR_SMS4));
}
/* unicast cipher list */
selcnt = frm;
WLAN_CRYPTO_ADDSHORT(frm, 0);
if (UCIPHER_IS_SMS4(crypto_params)) {
selcnt[0]++;
WLAN_CRYPTO_ADDSELECTOR(frm,
WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER));
}
WLAN_CRYPTO_ADDSELECTOR(frm,
WLAN_WAPI_SEL(WLAN_CRYPTO_WAPI_SMS4_CIPHER));
/* optional capabilities */
WLAN_CRYPTO_ADDSHORT(frm, crypto_params->rsn_caps);
/* calculate element length */
iebuf[1] = frm - iebuf - 2;
return frm;
}
/**
* wlan_crypto_pn_check - called by data patch for PN check
*
* @vdev: vdev
* @wbuf: wbuf
*
* This function gets called by data patch for PN check
*
* Return: QDF_STATUS
*/
QDF_STATUS wlan_crypto_pn_check(struct wlan_objmgr_vdev *vdev,
qdf_nbuf_t wbuf){
/* Need to check is there real requirement for this function
* as PN check is already handled in decap function.
*/
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_vdev_get_crypto_params - called by mlme to get crypto params
*
* @vdev:vdev
*
* This function gets called by mlme to get crypto params
*
* Return: wlan_crypto_params or NULL in case of failure
*/
struct wlan_crypto_params *wlan_crypto_vdev_get_crypto_params(
struct wlan_objmgr_vdev *vdev){
struct wlan_crypto_comp_priv *crypto_priv;
return wlan_crypto_vdev_get_comp_params(vdev, &crypto_priv);
}
/**
* wlan_crypto_peer_get_crypto_params - called by mlme to get crypto params
*
* @peer:peer
*
* This function gets called by mlme to get crypto params
*
* Return: wlan_crypto_params or NULL in case of failure
*/
struct wlan_crypto_params *wlan_crypto_peer_get_crypto_params(
struct wlan_objmgr_peer *peer){
struct wlan_crypto_comp_priv *crypto_priv;
return wlan_crypto_peer_get_comp_params(peer, &crypto_priv);
}
QDF_STATUS wlan_crypto_set_peer_wep_keys(struct wlan_objmgr_vdev *vdev,
uint8_t *mac_addr){
uint8_t keymac[WLAN_ALEN];
struct wlan_crypto_comp_priv *crypto_priv;
struct wlan_crypto_params *crypto_params;
struct wlan_crypto_key *key;
struct wlan_crypto_key *tmp_key;
struct wlan_crypto_cipher *cipher_table;
struct wlan_objmgr_psoc *psoc;
int i;
enum tQDF_ADAPTER_MODE opmode;
if (!vdev)
return QDF_STATUS_E_NULL_VALUE;
opmode = wlan_vdev_mlme_get_opmode(vdev);
psoc = wlan_vdev_get_psoc(vdev);
if (!psoc) {
qdf_print("%s[%d] psoc NULL\n", __func__, __LINE__);
return QDF_STATUS_E_NULL_VALUE;
}
crypto_params = wlan_crypto_vdev_get_comp_params(vdev,
&crypto_priv);
if (crypto_priv == NULL) {
qdf_print("%s[%d] crypto_priv NULL\n", __func__, __LINE__);
return QDF_STATUS_E_NULL_VALUE;
}
/* push only valid static WEP keys from vap */
if (AUTH_IS_8021X(crypto_params))
return QDF_STATUS_E_INVAL;
if (qdf_is_macaddr_broadcast((struct qdf_mac_addr *)mac_addr))
return QDF_STATUS_E_INVAL;
tmp_key = (struct wlan_crypto_key *)qdf_mem_malloc(
sizeof(struct wlan_crypto_key));
if (!tmp_key)
return QDF_STATUS_E_NULL_VALUE;
/* push only valid static WEP keys from vap */
for (i = 0; i < WLAN_CRYPTO_MAXKEYIDX; i++) {
if (crypto_priv->key[i]) {
key = crypto_priv->key[i];
if (!key || !key->valid)
continue;
cipher_table = (struct wlan_crypto_cipher *)
key->cipher_table;
if (cipher_table->cipher == WLAN_CRYPTO_CIPHER_WEP) {
qdf_mem_copy(tmp_key, key,
sizeof(struct wlan_crypto_key));
qdf_copy_macaddr((struct qdf_mac_addr *)keymac,
(struct qdf_mac_addr *)mac_addr);
tmp_key->flags &= ~WLAN_CRYPTO_KEY_DEFAULT;
if (crypto_priv->def_tx_keyid == i)
tmp_key->flags
|= WLAN_CRYPTO_KEY_DEFAULT;
/* setting the broadcast/multicast key for sta*/
if (opmode == QDF_STA_MODE ||
opmode == QDF_IBSS_MODE){
if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(
vdev, tmp_key, mac_addr,
cipher_table->cipher);
}
}
/* setting unicast key */
tmp_key->flags &= ~WLAN_CRYPTO_KEY_GROUP;
if (WLAN_CRYPTO_TX_OPS_SETKEY(psoc)) {
WLAN_CRYPTO_TX_OPS_SETKEY(psoc)(vdev,
tmp_key, mac_addr,
cipher_table->cipher);
}
}
}
}
qdf_mem_free(tmp_key);
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_register_crypto_rx_ops - set crypto_rx_ops
*
* @crypto_rx_ops: crypto_rx_ops
*
* This function gets called by object manger to register crypto rx ops.
*
* Return: QDF_STATUS
*/
QDF_STATUS wlan_crypto_register_crypto_rx_ops(
struct wlan_lmac_if_crypto_rx_ops *crypto_rx_ops){
crypto_rx_ops->crypto_encap = wlan_crypto_encap;
crypto_rx_ops->crypto_decap = wlan_crypto_decap;
crypto_rx_ops->crypto_enmic = wlan_crypto_enmic;
crypto_rx_ops->crypto_demic = wlan_crypto_demic;
crypto_rx_ops->set_peer_wep_keys = wlan_crypto_set_peer_wep_keys;
return QDF_STATUS_SUCCESS;
}
/**
* wlan_crypto_get_crypto_rx_ops - get crypto_rx_ops from psoc
*
* @psoc: psoc
*
* This function gets called by umac to get the crypto_rx_ops
*
* Return: crypto_rx_ops
*/
struct wlan_lmac_if_crypto_rx_ops *wlan_crypto_get_crypto_rx_ops(
struct wlan_objmgr_psoc *psoc)
{
return &(psoc->soc_cb.rx_ops.crypto_rx_ops);
}
EXPORT_SYMBOL(wlan_crypto_get_crypto_rx_ops);
/**
* wlan_crypto_vdev_has_auth_mode - check authmode for vdev
*
* @vdev: vdev
* @authvalue: authvalue to be checked
*
* This function check is authvalue passed is set in vdev or not
*
* Return: true or false
*/
bool wlan_crypto_vdev_has_auth_mode(struct wlan_objmgr_vdev *vdev,
wlan_crypto_auth_mode authvalue)
{
return wlan_crypto_get_param(vdev, WLAN_CRYPTO_PARAM_AUTH_MODE)
& authvalue;
}
/**
* wlan_crypto_peer_has_auth_mode - check authmode for peer
*
* @peer: peer
* @authvalue: authvalue to be checked
*
* This function check is authvalue passed is set in peer or not
*
* Return: true or false
*/
bool wlan_crypto_peer_has_auth_mode(struct wlan_objmgr_peer *peer,
wlan_crypto_auth_mode authvalue)
{
return wlan_crypto_get_peer_param(peer, WLAN_CRYPTO_PARAM_AUTH_MODE)
& authvalue;
}