// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014 Fraunhofer ITWM
*
* Written by:
* Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de>
*/
#include <linux/err.h>
#include <linux/bug.h>
#include <linux/completion.h>
#include <linux/ieee802154.h>
#include <linux/rculist.h>
#include <crypto/aead.h>
#include <crypto/skcipher.h>
#include "ieee802154_i.h"
#include "llsec.h"
static void llsec_key_put(struct mac802154_llsec_key *key);
static bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a,
const struct ieee802154_llsec_key_id *b);
static void llsec_dev_free(struct mac802154_llsec_device *dev);
void mac802154_llsec_init(struct mac802154_llsec *sec)
{
memset(sec, 0, sizeof(*sec));
memset(&sec->params.default_key_source, 0xFF, IEEE802154_ADDR_LEN);
INIT_LIST_HEAD(&sec->table.security_levels);
INIT_LIST_HEAD(&sec->table.devices);
INIT_LIST_HEAD(&sec->table.keys);
hash_init(sec->devices_short);
hash_init(sec->devices_hw);
rwlock_init(&sec->lock);
}
void mac802154_llsec_destroy(struct mac802154_llsec *sec)
{
struct ieee802154_llsec_seclevel *sl, *sn;
struct ieee802154_llsec_device *dev, *dn;
struct ieee802154_llsec_key_entry *key, *kn;
list_for_each_entry_safe(sl, sn, &sec->table.security_levels, list) {
struct mac802154_llsec_seclevel *msl;
msl = container_of(sl, struct mac802154_llsec_seclevel, level);
list_del(&sl->list);
kfree_sensitive(msl);
}
list_for_each_entry_safe(dev, dn, &sec->table.devices, list) {
struct mac802154_llsec_device *mdev;
mdev = container_of(dev, struct mac802154_llsec_device, dev);
list_del(&dev->list);
llsec_dev_free(mdev);
}
list_for_each_entry_safe(key, kn, &sec->table.keys, list) {
struct mac802154_llsec_key *mkey;
mkey = container_of(key->key, struct mac802154_llsec_key, key);
list_del(&key->list);
llsec_key_put(mkey);
kfree_sensitive(key);
}
}
int mac802154_llsec_get_params(struct mac802154_llsec *sec,
struct ieee802154_llsec_params *params)
{
read_lock_bh(&sec->lock);
*params = sec->params;
read_unlock_bh(&sec->lock);
return 0;
}
int mac802154_llsec_set_params(struct mac802154_llsec *sec,
const struct ieee802154_llsec_params *params,
int changed)
{
write_lock_bh(&sec->lock);
if (changed & IEEE802154_LLSEC_PARAM_ENABLED)
sec->params.enabled = params->enabled;
if (changed & IEEE802154_LLSEC_PARAM_FRAME_COUNTER)
sec->params.frame_counter = params->frame_counter;
if (changed & IEEE802154_LLSEC_PARAM_OUT_LEVEL)
sec->params.out_level = params->out_level;
if (changed & IEEE802154_LLSEC_PARAM_OUT_KEY)
sec->params.out_key = params->out_key;
if (changed & IEEE802154_LLSEC_PARAM_KEY_SOURCE)
sec->params.default_key_source = params->default_key_source;
if (changed & IEEE802154_LLSEC_PARAM_PAN_ID)
sec->params.pan_id = params->pan_id;
if (changed & IEEE802154_LLSEC_PARAM_HWADDR)
sec->params.hwaddr = params->hwaddr;
if (changed & IEEE802154_LLSEC_PARAM_COORD_HWADDR)
sec->params.coord_hwaddr = params->coord_hwaddr;
if (changed & IEEE802154_LLSEC_PARAM_COORD_SHORTADDR)
sec->params.coord_shortaddr = params->coord_shortaddr;
write_unlock_bh(&sec->lock);
return 0;
}
static struct mac802154_llsec_key*
llsec_key_alloc(const struct ieee802154_llsec_key *template)
{
const int authsizes[3] = { 4, 8, 16 };
struct mac802154_llsec_key *key;
int i;
key = kzalloc(sizeof(*key), GFP_KERNEL);
if (!key)
return NULL;
kref_init(&key->ref);
key->key = *template;
BUILD_BUG_ON(ARRAY_SIZE(authsizes) != ARRAY_SIZE(key->tfm));
for (i = 0; i < ARRAY_SIZE(key->tfm); i++) {
key->tfm[i] = crypto_alloc_aead("ccm(aes)", 0,
CRYPTO_ALG_ASYNC);
if (IS_ERR(key->tfm[i]))
goto err_tfm;
if (crypto_aead_setkey(key->tfm[i], template->key,
IEEE802154_LLSEC_KEY_SIZE))
goto err_tfm;
if (crypto_aead_setauthsize(key->tfm[i], authsizes[i]))
goto err_tfm;
}
key->tfm0 = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0);
if (IS_ERR(key->tfm0))
goto err_tfm;
if (crypto_sync_skcipher_setkey(key->tfm0, template->key,