162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2014 Fraunhofer ITWM 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Written by: 662306a36Sopenharmony_ci * Phoebe Buckheister <phoebe.buckheister@itwm.fraunhofer.de> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/err.h> 1062306a36Sopenharmony_ci#include <linux/bug.h> 1162306a36Sopenharmony_ci#include <linux/completion.h> 1262306a36Sopenharmony_ci#include <linux/ieee802154.h> 1362306a36Sopenharmony_ci#include <linux/rculist.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <crypto/aead.h> 1662306a36Sopenharmony_ci#include <crypto/skcipher.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "ieee802154_i.h" 1962306a36Sopenharmony_ci#include "llsec.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistatic void llsec_key_put(struct mac802154_llsec_key *key); 2262306a36Sopenharmony_cistatic bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a, 2362306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *b); 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cistatic void llsec_dev_free(struct mac802154_llsec_device *dev); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_civoid mac802154_llsec_init(struct mac802154_llsec *sec) 2862306a36Sopenharmony_ci{ 2962306a36Sopenharmony_ci memset(sec, 0, sizeof(*sec)); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci memset(&sec->params.default_key_source, 0xFF, IEEE802154_ADDR_LEN); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci INIT_LIST_HEAD(&sec->table.security_levels); 3462306a36Sopenharmony_ci INIT_LIST_HEAD(&sec->table.devices); 3562306a36Sopenharmony_ci INIT_LIST_HEAD(&sec->table.keys); 3662306a36Sopenharmony_ci hash_init(sec->devices_short); 3762306a36Sopenharmony_ci hash_init(sec->devices_hw); 3862306a36Sopenharmony_ci rwlock_init(&sec->lock); 3962306a36Sopenharmony_ci} 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_civoid mac802154_llsec_destroy(struct mac802154_llsec *sec) 4262306a36Sopenharmony_ci{ 4362306a36Sopenharmony_ci struct ieee802154_llsec_seclevel *sl, *sn; 4462306a36Sopenharmony_ci struct ieee802154_llsec_device *dev, *dn; 4562306a36Sopenharmony_ci struct ieee802154_llsec_key_entry *key, *kn; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci list_for_each_entry_safe(sl, sn, &sec->table.security_levels, list) { 4862306a36Sopenharmony_ci struct mac802154_llsec_seclevel *msl; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci msl = container_of(sl, struct mac802154_llsec_seclevel, level); 5162306a36Sopenharmony_ci list_del(&sl->list); 5262306a36Sopenharmony_ci kfree_sensitive(msl); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci list_for_each_entry_safe(dev, dn, &sec->table.devices, list) { 5662306a36Sopenharmony_ci struct mac802154_llsec_device *mdev; 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci mdev = container_of(dev, struct mac802154_llsec_device, dev); 5962306a36Sopenharmony_ci list_del(&dev->list); 6062306a36Sopenharmony_ci llsec_dev_free(mdev); 6162306a36Sopenharmony_ci } 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci list_for_each_entry_safe(key, kn, &sec->table.keys, list) { 6462306a36Sopenharmony_ci struct mac802154_llsec_key *mkey; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci mkey = container_of(key->key, struct mac802154_llsec_key, key); 6762306a36Sopenharmony_ci list_del(&key->list); 6862306a36Sopenharmony_ci llsec_key_put(mkey); 6962306a36Sopenharmony_ci kfree_sensitive(key); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci} 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ciint mac802154_llsec_get_params(struct mac802154_llsec *sec, 7462306a36Sopenharmony_ci struct ieee802154_llsec_params *params) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci read_lock_bh(&sec->lock); 7762306a36Sopenharmony_ci *params = sec->params; 7862306a36Sopenharmony_ci read_unlock_bh(&sec->lock); 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ciint mac802154_llsec_set_params(struct mac802154_llsec *sec, 8462306a36Sopenharmony_ci const struct ieee802154_llsec_params *params, 8562306a36Sopenharmony_ci int changed) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci write_lock_bh(&sec->lock); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_ENABLED) 9062306a36Sopenharmony_ci sec->params.enabled = params->enabled; 9162306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_FRAME_COUNTER) 9262306a36Sopenharmony_ci sec->params.frame_counter = params->frame_counter; 9362306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_OUT_LEVEL) 9462306a36Sopenharmony_ci sec->params.out_level = params->out_level; 9562306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_OUT_KEY) 9662306a36Sopenharmony_ci sec->params.out_key = params->out_key; 9762306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_KEY_SOURCE) 9862306a36Sopenharmony_ci sec->params.default_key_source = params->default_key_source; 9962306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_PAN_ID) 10062306a36Sopenharmony_ci sec->params.pan_id = params->pan_id; 10162306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_HWADDR) 10262306a36Sopenharmony_ci sec->params.hwaddr = params->hwaddr; 10362306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_COORD_HWADDR) 10462306a36Sopenharmony_ci sec->params.coord_hwaddr = params->coord_hwaddr; 10562306a36Sopenharmony_ci if (changed & IEEE802154_LLSEC_PARAM_COORD_SHORTADDR) 10662306a36Sopenharmony_ci sec->params.coord_shortaddr = params->coord_shortaddr; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci write_unlock_bh(&sec->lock); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci return 0; 11162306a36Sopenharmony_ci} 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_cistatic struct mac802154_llsec_key* 11462306a36Sopenharmony_cillsec_key_alloc(const struct ieee802154_llsec_key *template) 11562306a36Sopenharmony_ci{ 11662306a36Sopenharmony_ci const int authsizes[3] = { 4, 8, 16 }; 11762306a36Sopenharmony_ci struct mac802154_llsec_key *key; 11862306a36Sopenharmony_ci int i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci key = kzalloc(sizeof(*key), GFP_KERNEL); 12162306a36Sopenharmony_ci if (!key) 12262306a36Sopenharmony_ci return NULL; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci kref_init(&key->ref); 12562306a36Sopenharmony_ci key->key = *template; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci BUILD_BUG_ON(ARRAY_SIZE(authsizes) != ARRAY_SIZE(key->tfm)); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(key->tfm); i++) { 13062306a36Sopenharmony_ci key->tfm[i] = crypto_alloc_aead("ccm(aes)", 0, 13162306a36Sopenharmony_ci CRYPTO_ALG_ASYNC); 13262306a36Sopenharmony_ci if (IS_ERR(key->tfm[i])) 13362306a36Sopenharmony_ci goto err_tfm; 13462306a36Sopenharmony_ci if (crypto_aead_setkey(key->tfm[i], template->key, 13562306a36Sopenharmony_ci IEEE802154_LLSEC_KEY_SIZE)) 13662306a36Sopenharmony_ci goto err_tfm; 13762306a36Sopenharmony_ci if (crypto_aead_setauthsize(key->tfm[i], authsizes[i])) 13862306a36Sopenharmony_ci goto err_tfm; 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci key->tfm0 = crypto_alloc_sync_skcipher("ctr(aes)", 0, 0); 14262306a36Sopenharmony_ci if (IS_ERR(key->tfm0)) 14362306a36Sopenharmony_ci goto err_tfm; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (crypto_sync_skcipher_setkey(key->tfm0, template->key, 14662306a36Sopenharmony_ci IEEE802154_LLSEC_KEY_SIZE)) 14762306a36Sopenharmony_ci goto err_tfm0; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return key; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cierr_tfm0: 15262306a36Sopenharmony_ci crypto_free_sync_skcipher(key->tfm0); 15362306a36Sopenharmony_cierr_tfm: 15462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(key->tfm); i++) 15562306a36Sopenharmony_ci if (!IS_ERR_OR_NULL(key->tfm[i])) 15662306a36Sopenharmony_ci crypto_free_aead(key->tfm[i]); 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci kfree_sensitive(key); 15962306a36Sopenharmony_ci return NULL; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void llsec_key_release(struct kref *ref) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci struct mac802154_llsec_key *key; 16562306a36Sopenharmony_ci int i; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci key = container_of(ref, struct mac802154_llsec_key, ref); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(key->tfm); i++) 17062306a36Sopenharmony_ci crypto_free_aead(key->tfm[i]); 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci crypto_free_sync_skcipher(key->tfm0); 17362306a36Sopenharmony_ci kfree_sensitive(key); 17462306a36Sopenharmony_ci} 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct mac802154_llsec_key* 17762306a36Sopenharmony_cillsec_key_get(struct mac802154_llsec_key *key) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci kref_get(&key->ref); 18062306a36Sopenharmony_ci return key; 18162306a36Sopenharmony_ci} 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_cistatic void llsec_key_put(struct mac802154_llsec_key *key) 18462306a36Sopenharmony_ci{ 18562306a36Sopenharmony_ci kref_put(&key->ref, llsec_key_release); 18662306a36Sopenharmony_ci} 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic bool llsec_key_id_equal(const struct ieee802154_llsec_key_id *a, 18962306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *b) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci if (a->mode != b->mode) 19262306a36Sopenharmony_ci return false; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (a->mode == IEEE802154_SCF_KEY_IMPLICIT) 19562306a36Sopenharmony_ci return ieee802154_addr_equal(&a->device_addr, &b->device_addr); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci if (a->id != b->id) 19862306a36Sopenharmony_ci return false; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci switch (a->mode) { 20162306a36Sopenharmony_ci case IEEE802154_SCF_KEY_INDEX: 20262306a36Sopenharmony_ci return true; 20362306a36Sopenharmony_ci case IEEE802154_SCF_KEY_SHORT_INDEX: 20462306a36Sopenharmony_ci return a->short_source == b->short_source; 20562306a36Sopenharmony_ci case IEEE802154_SCF_KEY_HW_INDEX: 20662306a36Sopenharmony_ci return a->extended_source == b->extended_source; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci return false; 21062306a36Sopenharmony_ci} 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ciint mac802154_llsec_key_add(struct mac802154_llsec *sec, 21362306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *id, 21462306a36Sopenharmony_ci const struct ieee802154_llsec_key *key) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci struct mac802154_llsec_key *mkey = NULL; 21762306a36Sopenharmony_ci struct ieee802154_llsec_key_entry *pos, *new; 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci if (!(key->frame_types & (1 << IEEE802154_FC_TYPE_MAC_CMD)) && 22062306a36Sopenharmony_ci key->cmd_frame_ids) 22162306a36Sopenharmony_ci return -EINVAL; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci list_for_each_entry(pos, &sec->table.keys, list) { 22462306a36Sopenharmony_ci if (llsec_key_id_equal(&pos->id, id)) 22562306a36Sopenharmony_ci return -EEXIST; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci if (memcmp(pos->key->key, key->key, 22862306a36Sopenharmony_ci IEEE802154_LLSEC_KEY_SIZE)) 22962306a36Sopenharmony_ci continue; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci mkey = container_of(pos->key, struct mac802154_llsec_key, key); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* Don't allow multiple instances of the same AES key to have 23462306a36Sopenharmony_ci * different allowed frame types/command frame ids, as this is 23562306a36Sopenharmony_ci * not possible in the 802.15.4 PIB. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (pos->key->frame_types != key->frame_types || 23862306a36Sopenharmony_ci pos->key->cmd_frame_ids != key->cmd_frame_ids) 23962306a36Sopenharmony_ci return -EEXIST; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci break; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci new = kzalloc(sizeof(*new), GFP_KERNEL); 24562306a36Sopenharmony_ci if (!new) 24662306a36Sopenharmony_ci return -ENOMEM; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!mkey) 24962306a36Sopenharmony_ci mkey = llsec_key_alloc(key); 25062306a36Sopenharmony_ci else 25162306a36Sopenharmony_ci mkey = llsec_key_get(mkey); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (!mkey) 25462306a36Sopenharmony_ci goto fail; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci new->id = *id; 25762306a36Sopenharmony_ci new->key = &mkey->key; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci list_add_rcu(&new->list, &sec->table.keys); 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci return 0; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_cifail: 26462306a36Sopenharmony_ci kfree_sensitive(new); 26562306a36Sopenharmony_ci return -ENOMEM; 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic void mac802154_llsec_key_del_rcu(struct rcu_head *rcu) 26962306a36Sopenharmony_ci{ 27062306a36Sopenharmony_ci struct ieee802154_llsec_key_entry *pos; 27162306a36Sopenharmony_ci struct mac802154_llsec_key *mkey; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci pos = container_of(rcu, struct ieee802154_llsec_key_entry, rcu); 27462306a36Sopenharmony_ci mkey = container_of(pos->key, struct mac802154_llsec_key, key); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci llsec_key_put(mkey); 27762306a36Sopenharmony_ci kfree_sensitive(pos); 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ciint mac802154_llsec_key_del(struct mac802154_llsec *sec, 28162306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *key) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci struct ieee802154_llsec_key_entry *pos; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci list_for_each_entry(pos, &sec->table.keys, list) { 28662306a36Sopenharmony_ci if (llsec_key_id_equal(&pos->id, key)) { 28762306a36Sopenharmony_ci list_del_rcu(&pos->list); 28862306a36Sopenharmony_ci call_rcu(&pos->rcu, mac802154_llsec_key_del_rcu); 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci return -ENOENT; 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_cistatic bool llsec_dev_use_shortaddr(__le16 short_addr) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci return short_addr != cpu_to_le16(IEEE802154_ADDR_UNDEF) && 29962306a36Sopenharmony_ci short_addr != cpu_to_le16(0xffff); 30062306a36Sopenharmony_ci} 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_cistatic u32 llsec_dev_hash_short(__le16 short_addr, __le16 pan_id) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci return ((__force u16)short_addr) << 16 | (__force u16)pan_id; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic u64 llsec_dev_hash_long(__le64 hwaddr) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci return (__force u64)hwaddr; 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic struct mac802154_llsec_device* 31362306a36Sopenharmony_cillsec_dev_find_short(struct mac802154_llsec *sec, __le16 short_addr, 31462306a36Sopenharmony_ci __le16 pan_id) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct mac802154_llsec_device *dev; 31762306a36Sopenharmony_ci u32 key = llsec_dev_hash_short(short_addr, pan_id); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci hash_for_each_possible_rcu(sec->devices_short, dev, bucket_s, key) { 32062306a36Sopenharmony_ci if (dev->dev.short_addr == short_addr && 32162306a36Sopenharmony_ci dev->dev.pan_id == pan_id) 32262306a36Sopenharmony_ci return dev; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return NULL; 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic struct mac802154_llsec_device* 32962306a36Sopenharmony_cillsec_dev_find_long(struct mac802154_llsec *sec, __le64 hwaddr) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci struct mac802154_llsec_device *dev; 33262306a36Sopenharmony_ci u64 key = llsec_dev_hash_long(hwaddr); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci hash_for_each_possible_rcu(sec->devices_hw, dev, bucket_hw, key) { 33562306a36Sopenharmony_ci if (dev->dev.hwaddr == hwaddr) 33662306a36Sopenharmony_ci return dev; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci return NULL; 34062306a36Sopenharmony_ci} 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_cistatic void llsec_dev_free(struct mac802154_llsec_device *dev) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct ieee802154_llsec_device_key *pos, *pn; 34562306a36Sopenharmony_ci struct mac802154_llsec_device_key *devkey; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci list_for_each_entry_safe(pos, pn, &dev->dev.keys, list) { 34862306a36Sopenharmony_ci devkey = container_of(pos, struct mac802154_llsec_device_key, 34962306a36Sopenharmony_ci devkey); 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci list_del(&pos->list); 35262306a36Sopenharmony_ci kfree_sensitive(devkey); 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci kfree_sensitive(dev); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ciint mac802154_llsec_dev_add(struct mac802154_llsec *sec, 35962306a36Sopenharmony_ci const struct ieee802154_llsec_device *dev) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct mac802154_llsec_device *entry; 36262306a36Sopenharmony_ci u32 skey = llsec_dev_hash_short(dev->short_addr, dev->pan_id); 36362306a36Sopenharmony_ci u64 hwkey = llsec_dev_hash_long(dev->hwaddr); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(hwkey) != IEEE802154_ADDR_LEN); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci if ((llsec_dev_use_shortaddr(dev->short_addr) && 36862306a36Sopenharmony_ci llsec_dev_find_short(sec, dev->short_addr, dev->pan_id)) || 36962306a36Sopenharmony_ci llsec_dev_find_long(sec, dev->hwaddr)) 37062306a36Sopenharmony_ci return -EEXIST; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 37362306a36Sopenharmony_ci if (!entry) 37462306a36Sopenharmony_ci return -ENOMEM; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci entry->dev = *dev; 37762306a36Sopenharmony_ci spin_lock_init(&entry->lock); 37862306a36Sopenharmony_ci INIT_LIST_HEAD(&entry->dev.keys); 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci if (llsec_dev_use_shortaddr(dev->short_addr)) 38162306a36Sopenharmony_ci hash_add_rcu(sec->devices_short, &entry->bucket_s, skey); 38262306a36Sopenharmony_ci else 38362306a36Sopenharmony_ci INIT_HLIST_NODE(&entry->bucket_s); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci hash_add_rcu(sec->devices_hw, &entry->bucket_hw, hwkey); 38662306a36Sopenharmony_ci list_add_tail_rcu(&entry->dev.list, &sec->table.devices); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic void llsec_dev_free_rcu(struct rcu_head *rcu) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci llsec_dev_free(container_of(rcu, struct mac802154_llsec_device, rcu)); 39462306a36Sopenharmony_ci} 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ciint mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr) 39762306a36Sopenharmony_ci{ 39862306a36Sopenharmony_ci struct mac802154_llsec_device *pos; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci pos = llsec_dev_find_long(sec, device_addr); 40162306a36Sopenharmony_ci if (!pos) 40262306a36Sopenharmony_ci return -ENOENT; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci hash_del_rcu(&pos->bucket_s); 40562306a36Sopenharmony_ci hash_del_rcu(&pos->bucket_hw); 40662306a36Sopenharmony_ci list_del_rcu(&pos->dev.list); 40762306a36Sopenharmony_ci call_rcu(&pos->rcu, llsec_dev_free_rcu); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic struct mac802154_llsec_device_key* 41362306a36Sopenharmony_cillsec_devkey_find(struct mac802154_llsec_device *dev, 41462306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *key) 41562306a36Sopenharmony_ci{ 41662306a36Sopenharmony_ci struct ieee802154_llsec_device_key *devkey; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci list_for_each_entry_rcu(devkey, &dev->dev.keys, list) { 41962306a36Sopenharmony_ci if (!llsec_key_id_equal(key, &devkey->key_id)) 42062306a36Sopenharmony_ci continue; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci return container_of(devkey, struct mac802154_llsec_device_key, 42362306a36Sopenharmony_ci devkey); 42462306a36Sopenharmony_ci } 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci return NULL; 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ciint mac802154_llsec_devkey_add(struct mac802154_llsec *sec, 43062306a36Sopenharmony_ci __le64 dev_addr, 43162306a36Sopenharmony_ci const struct ieee802154_llsec_device_key *key) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct mac802154_llsec_device *dev; 43462306a36Sopenharmony_ci struct mac802154_llsec_device_key *devkey; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci dev = llsec_dev_find_long(sec, dev_addr); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci if (!dev) 43962306a36Sopenharmony_ci return -ENOENT; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (llsec_devkey_find(dev, &key->key_id)) 44262306a36Sopenharmony_ci return -EEXIST; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci devkey = kmalloc(sizeof(*devkey), GFP_KERNEL); 44562306a36Sopenharmony_ci if (!devkey) 44662306a36Sopenharmony_ci return -ENOMEM; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci devkey->devkey = *key; 44962306a36Sopenharmony_ci list_add_tail_rcu(&devkey->devkey.list, &dev->dev.keys); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ciint mac802154_llsec_devkey_del(struct mac802154_llsec *sec, 45462306a36Sopenharmony_ci __le64 dev_addr, 45562306a36Sopenharmony_ci const struct ieee802154_llsec_device_key *key) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct mac802154_llsec_device *dev; 45862306a36Sopenharmony_ci struct mac802154_llsec_device_key *devkey; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci dev = llsec_dev_find_long(sec, dev_addr); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci if (!dev) 46362306a36Sopenharmony_ci return -ENOENT; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci devkey = llsec_devkey_find(dev, &key->key_id); 46662306a36Sopenharmony_ci if (!devkey) 46762306a36Sopenharmony_ci return -ENOENT; 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci list_del_rcu(&devkey->devkey.list); 47062306a36Sopenharmony_ci kfree_rcu(devkey, rcu); 47162306a36Sopenharmony_ci return 0; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic struct mac802154_llsec_seclevel* 47562306a36Sopenharmony_cillsec_find_seclevel(const struct mac802154_llsec *sec, 47662306a36Sopenharmony_ci const struct ieee802154_llsec_seclevel *sl) 47762306a36Sopenharmony_ci{ 47862306a36Sopenharmony_ci struct ieee802154_llsec_seclevel *pos; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci list_for_each_entry(pos, &sec->table.security_levels, list) { 48162306a36Sopenharmony_ci if (pos->frame_type != sl->frame_type || 48262306a36Sopenharmony_ci (pos->frame_type == IEEE802154_FC_TYPE_MAC_CMD && 48362306a36Sopenharmony_ci pos->cmd_frame_id != sl->cmd_frame_id) || 48462306a36Sopenharmony_ci pos->device_override != sl->device_override || 48562306a36Sopenharmony_ci pos->sec_levels != sl->sec_levels) 48662306a36Sopenharmony_ci continue; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci return container_of(pos, struct mac802154_llsec_seclevel, 48962306a36Sopenharmony_ci level); 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci return NULL; 49362306a36Sopenharmony_ci} 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ciint mac802154_llsec_seclevel_add(struct mac802154_llsec *sec, 49662306a36Sopenharmony_ci const struct ieee802154_llsec_seclevel *sl) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct mac802154_llsec_seclevel *entry; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci if (llsec_find_seclevel(sec, sl)) 50162306a36Sopenharmony_ci return -EEXIST; 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci entry = kmalloc(sizeof(*entry), GFP_KERNEL); 50462306a36Sopenharmony_ci if (!entry) 50562306a36Sopenharmony_ci return -ENOMEM; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci entry->level = *sl; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci list_add_tail_rcu(&entry->level.list, &sec->table.security_levels); 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci return 0; 51262306a36Sopenharmony_ci} 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ciint mac802154_llsec_seclevel_del(struct mac802154_llsec *sec, 51562306a36Sopenharmony_ci const struct ieee802154_llsec_seclevel *sl) 51662306a36Sopenharmony_ci{ 51762306a36Sopenharmony_ci struct mac802154_llsec_seclevel *pos; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci pos = llsec_find_seclevel(sec, sl); 52062306a36Sopenharmony_ci if (!pos) 52162306a36Sopenharmony_ci return -ENOENT; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci list_del_rcu(&pos->level.list); 52462306a36Sopenharmony_ci kfree_rcu(pos, rcu); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci return 0; 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int llsec_recover_addr(struct mac802154_llsec *sec, 53062306a36Sopenharmony_ci struct ieee802154_addr *addr) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci __le16 caddr = sec->params.coord_shortaddr; 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci addr->pan_id = sec->params.pan_id; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (caddr == cpu_to_le16(IEEE802154_ADDR_BROADCAST)) { 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci } else if (caddr == cpu_to_le16(IEEE802154_ADDR_UNDEF)) { 53962306a36Sopenharmony_ci addr->extended_addr = sec->params.coord_hwaddr; 54062306a36Sopenharmony_ci addr->mode = IEEE802154_ADDR_LONG; 54162306a36Sopenharmony_ci } else { 54262306a36Sopenharmony_ci addr->short_addr = sec->params.coord_shortaddr; 54362306a36Sopenharmony_ci addr->mode = IEEE802154_ADDR_SHORT; 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci return 0; 54762306a36Sopenharmony_ci} 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_cistatic struct mac802154_llsec_key* 55062306a36Sopenharmony_cillsec_lookup_key(struct mac802154_llsec *sec, 55162306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 55262306a36Sopenharmony_ci const struct ieee802154_addr *addr, 55362306a36Sopenharmony_ci struct ieee802154_llsec_key_id *key_id) 55462306a36Sopenharmony_ci{ 55562306a36Sopenharmony_ci struct ieee802154_addr devaddr = *addr; 55662306a36Sopenharmony_ci u8 key_id_mode = hdr->sec.key_id_mode; 55762306a36Sopenharmony_ci struct ieee802154_llsec_key_entry *key_entry; 55862306a36Sopenharmony_ci struct mac802154_llsec_key *key; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT && 56162306a36Sopenharmony_ci devaddr.mode == IEEE802154_ADDR_NONE) { 56262306a36Sopenharmony_ci if (hdr->fc.type == IEEE802154_FC_TYPE_BEACON) { 56362306a36Sopenharmony_ci devaddr.extended_addr = sec->params.coord_hwaddr; 56462306a36Sopenharmony_ci devaddr.mode = IEEE802154_ADDR_LONG; 56562306a36Sopenharmony_ci } else if (llsec_recover_addr(sec, &devaddr) < 0) { 56662306a36Sopenharmony_ci return NULL; 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci list_for_each_entry_rcu(key_entry, &sec->table.keys, list) { 57162306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *id = &key_entry->id; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci if (!(key_entry->key->frame_types & BIT(hdr->fc.type))) 57462306a36Sopenharmony_ci continue; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (id->mode != key_id_mode) 57762306a36Sopenharmony_ci continue; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (key_id_mode == IEEE802154_SCF_KEY_IMPLICIT) { 58062306a36Sopenharmony_ci if (ieee802154_addr_equal(&devaddr, &id->device_addr)) 58162306a36Sopenharmony_ci goto found; 58262306a36Sopenharmony_ci } else { 58362306a36Sopenharmony_ci if (id->id != hdr->sec.key_id) 58462306a36Sopenharmony_ci continue; 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci if ((key_id_mode == IEEE802154_SCF_KEY_INDEX) || 58762306a36Sopenharmony_ci (key_id_mode == IEEE802154_SCF_KEY_SHORT_INDEX && 58862306a36Sopenharmony_ci id->short_source == hdr->sec.short_src) || 58962306a36Sopenharmony_ci (key_id_mode == IEEE802154_SCF_KEY_HW_INDEX && 59062306a36Sopenharmony_ci id->extended_source == hdr->sec.extended_src)) 59162306a36Sopenharmony_ci goto found; 59262306a36Sopenharmony_ci } 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return NULL; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cifound: 59862306a36Sopenharmony_ci key = container_of(key_entry->key, struct mac802154_llsec_key, key); 59962306a36Sopenharmony_ci if (key_id) 60062306a36Sopenharmony_ci *key_id = key_entry->id; 60162306a36Sopenharmony_ci return llsec_key_get(key); 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void llsec_geniv(u8 iv[16], __le64 addr, 60562306a36Sopenharmony_ci const struct ieee802154_sechdr *sec) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci __be64 addr_bytes = (__force __be64) swab64((__force u64) addr); 60862306a36Sopenharmony_ci __be32 frame_counter = (__force __be32) swab32((__force u32) sec->frame_counter); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci iv[0] = 1; /* L' = L - 1 = 1 */ 61162306a36Sopenharmony_ci memcpy(iv + 1, &addr_bytes, sizeof(addr_bytes)); 61262306a36Sopenharmony_ci memcpy(iv + 9, &frame_counter, sizeof(frame_counter)); 61362306a36Sopenharmony_ci iv[13] = sec->level; 61462306a36Sopenharmony_ci iv[14] = 0; 61562306a36Sopenharmony_ci iv[15] = 1; 61662306a36Sopenharmony_ci} 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_cistatic int 61962306a36Sopenharmony_cillsec_do_encrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, 62062306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 62162306a36Sopenharmony_ci struct mac802154_llsec_key *key) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci u8 iv[16]; 62462306a36Sopenharmony_ci struct scatterlist src; 62562306a36Sopenharmony_ci SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm0); 62662306a36Sopenharmony_ci int err, datalen; 62762306a36Sopenharmony_ci unsigned char *data; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); 63062306a36Sopenharmony_ci /* Compute data payload offset and data length */ 63162306a36Sopenharmony_ci data = skb_mac_header(skb) + skb->mac_len; 63262306a36Sopenharmony_ci datalen = skb_tail_pointer(skb) - data; 63362306a36Sopenharmony_ci sg_init_one(&src, data, datalen); 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, key->tfm0); 63662306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 63762306a36Sopenharmony_ci skcipher_request_set_crypt(req, &src, &src, datalen, iv); 63862306a36Sopenharmony_ci err = crypto_skcipher_encrypt(req); 63962306a36Sopenharmony_ci skcipher_request_zero(req); 64062306a36Sopenharmony_ci return err; 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic struct crypto_aead* 64462306a36Sopenharmony_cillsec_tfm_by_len(struct mac802154_llsec_key *key, int authlen) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci int i; 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(key->tfm); i++) 64962306a36Sopenharmony_ci if (crypto_aead_authsize(key->tfm[i]) == authlen) 65062306a36Sopenharmony_ci return key->tfm[i]; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci BUG(); 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int 65662306a36Sopenharmony_cillsec_do_encrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, 65762306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 65862306a36Sopenharmony_ci struct mac802154_llsec_key *key) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci u8 iv[16]; 66162306a36Sopenharmony_ci unsigned char *data; 66262306a36Sopenharmony_ci int authlen, assoclen, datalen, rc; 66362306a36Sopenharmony_ci struct scatterlist sg; 66462306a36Sopenharmony_ci struct aead_request *req; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci authlen = ieee802154_sechdr_authtag_len(&hdr->sec); 66762306a36Sopenharmony_ci llsec_geniv(iv, sec->params.hwaddr, &hdr->sec); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); 67062306a36Sopenharmony_ci if (!req) 67162306a36Sopenharmony_ci return -ENOMEM; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci assoclen = skb->mac_len; 67462306a36Sopenharmony_ci 67562306a36Sopenharmony_ci data = skb_mac_header(skb) + skb->mac_len; 67662306a36Sopenharmony_ci datalen = skb_tail_pointer(skb) - data; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci skb_put(skb, authlen); 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen + authlen); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) { 68362306a36Sopenharmony_ci assoclen += datalen; 68462306a36Sopenharmony_ci datalen = 0; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci aead_request_set_callback(req, 0, NULL, NULL); 68862306a36Sopenharmony_ci aead_request_set_crypt(req, &sg, &sg, datalen, iv); 68962306a36Sopenharmony_ci aead_request_set_ad(req, assoclen); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci rc = crypto_aead_encrypt(req); 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci kfree_sensitive(req); 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci return rc; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_cistatic int llsec_do_encrypt(struct sk_buff *skb, 69962306a36Sopenharmony_ci const struct mac802154_llsec *sec, 70062306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 70162306a36Sopenharmony_ci struct mac802154_llsec_key *key) 70262306a36Sopenharmony_ci{ 70362306a36Sopenharmony_ci if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) 70462306a36Sopenharmony_ci return llsec_do_encrypt_unauth(skb, sec, hdr, key); 70562306a36Sopenharmony_ci else 70662306a36Sopenharmony_ci return llsec_do_encrypt_auth(skb, sec, hdr, key); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ciint mac802154_llsec_encrypt(struct mac802154_llsec *sec, struct sk_buff *skb) 71062306a36Sopenharmony_ci{ 71162306a36Sopenharmony_ci struct ieee802154_hdr hdr; 71262306a36Sopenharmony_ci int rc, authlen, hlen; 71362306a36Sopenharmony_ci struct mac802154_llsec_key *key; 71462306a36Sopenharmony_ci u32 frame_ctr; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci hlen = ieee802154_hdr_pull(skb, &hdr); 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci /* TODO: control frames security support */ 71962306a36Sopenharmony_ci if (hlen < 0 || 72062306a36Sopenharmony_ci (hdr.fc.type != IEEE802154_FC_TYPE_DATA && 72162306a36Sopenharmony_ci hdr.fc.type != IEEE802154_FC_TYPE_BEACON)) 72262306a36Sopenharmony_ci return -EINVAL; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci if (!hdr.fc.security_enabled || 72562306a36Sopenharmony_ci (hdr.sec.level == IEEE802154_SCF_SECLEVEL_NONE)) { 72662306a36Sopenharmony_ci skb_push(skb, hlen); 72762306a36Sopenharmony_ci return 0; 72862306a36Sopenharmony_ci } 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci authlen = ieee802154_sechdr_authtag_len(&hdr.sec); 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci if (skb->len + hlen + authlen + IEEE802154_MFR_SIZE > IEEE802154_MTU) 73362306a36Sopenharmony_ci return -EMSGSIZE; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci rcu_read_lock(); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci read_lock_bh(&sec->lock); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci if (!sec->params.enabled) { 74062306a36Sopenharmony_ci rc = -EINVAL; 74162306a36Sopenharmony_ci goto fail_read; 74262306a36Sopenharmony_ci } 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci key = llsec_lookup_key(sec, &hdr, &hdr.dest, NULL); 74562306a36Sopenharmony_ci if (!key) { 74662306a36Sopenharmony_ci rc = -ENOKEY; 74762306a36Sopenharmony_ci goto fail_read; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci read_unlock_bh(&sec->lock); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci write_lock_bh(&sec->lock); 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci frame_ctr = be32_to_cpu(sec->params.frame_counter); 75562306a36Sopenharmony_ci hdr.sec.frame_counter = cpu_to_le32(frame_ctr); 75662306a36Sopenharmony_ci if (frame_ctr == 0xFFFFFFFF) { 75762306a36Sopenharmony_ci write_unlock_bh(&sec->lock); 75862306a36Sopenharmony_ci llsec_key_put(key); 75962306a36Sopenharmony_ci rc = -EOVERFLOW; 76062306a36Sopenharmony_ci goto fail; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci sec->params.frame_counter = cpu_to_be32(frame_ctr + 1); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci write_unlock_bh(&sec->lock); 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci rcu_read_unlock(); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci skb->mac_len = ieee802154_hdr_push(skb, &hdr); 77062306a36Sopenharmony_ci skb_reset_mac_header(skb); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci rc = llsec_do_encrypt(skb, sec, &hdr, key); 77362306a36Sopenharmony_ci llsec_key_put(key); 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci return rc; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_cifail_read: 77862306a36Sopenharmony_ci read_unlock_bh(&sec->lock); 77962306a36Sopenharmony_cifail: 78062306a36Sopenharmony_ci rcu_read_unlock(); 78162306a36Sopenharmony_ci return rc; 78262306a36Sopenharmony_ci} 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_cistatic struct mac802154_llsec_device* 78562306a36Sopenharmony_cillsec_lookup_dev(struct mac802154_llsec *sec, 78662306a36Sopenharmony_ci const struct ieee802154_addr *addr) 78762306a36Sopenharmony_ci{ 78862306a36Sopenharmony_ci struct ieee802154_addr devaddr = *addr; 78962306a36Sopenharmony_ci struct mac802154_llsec_device *dev = NULL; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (devaddr.mode == IEEE802154_ADDR_NONE && 79262306a36Sopenharmony_ci llsec_recover_addr(sec, &devaddr) < 0) 79362306a36Sopenharmony_ci return NULL; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci if (devaddr.mode == IEEE802154_ADDR_SHORT) { 79662306a36Sopenharmony_ci u32 key = llsec_dev_hash_short(devaddr.short_addr, 79762306a36Sopenharmony_ci devaddr.pan_id); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci hash_for_each_possible_rcu(sec->devices_short, dev, 80062306a36Sopenharmony_ci bucket_s, key) { 80162306a36Sopenharmony_ci if (dev->dev.pan_id == devaddr.pan_id && 80262306a36Sopenharmony_ci dev->dev.short_addr == devaddr.short_addr) 80362306a36Sopenharmony_ci return dev; 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci } else { 80662306a36Sopenharmony_ci u64 key = llsec_dev_hash_long(devaddr.extended_addr); 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci hash_for_each_possible_rcu(sec->devices_hw, dev, 80962306a36Sopenharmony_ci bucket_hw, key) { 81062306a36Sopenharmony_ci if (dev->dev.hwaddr == devaddr.extended_addr) 81162306a36Sopenharmony_ci return dev; 81262306a36Sopenharmony_ci } 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci return NULL; 81662306a36Sopenharmony_ci} 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_cistatic int 81962306a36Sopenharmony_cillsec_lookup_seclevel(const struct mac802154_llsec *sec, 82062306a36Sopenharmony_ci u8 frame_type, u8 cmd_frame_id, 82162306a36Sopenharmony_ci struct ieee802154_llsec_seclevel *rlevel) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci struct ieee802154_llsec_seclevel *level; 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci list_for_each_entry_rcu(level, &sec->table.security_levels, list) { 82662306a36Sopenharmony_ci if (level->frame_type == frame_type && 82762306a36Sopenharmony_ci (frame_type != IEEE802154_FC_TYPE_MAC_CMD || 82862306a36Sopenharmony_ci level->cmd_frame_id == cmd_frame_id)) { 82962306a36Sopenharmony_ci *rlevel = *level; 83062306a36Sopenharmony_ci return 0; 83162306a36Sopenharmony_ci } 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return -EINVAL; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int 83862306a36Sopenharmony_cillsec_do_decrypt_unauth(struct sk_buff *skb, const struct mac802154_llsec *sec, 83962306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 84062306a36Sopenharmony_ci struct mac802154_llsec_key *key, __le64 dev_addr) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci u8 iv[16]; 84362306a36Sopenharmony_ci unsigned char *data; 84462306a36Sopenharmony_ci int datalen; 84562306a36Sopenharmony_ci struct scatterlist src; 84662306a36Sopenharmony_ci SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm0); 84762306a36Sopenharmony_ci int err; 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci llsec_geniv(iv, dev_addr, &hdr->sec); 85062306a36Sopenharmony_ci data = skb_mac_header(skb) + skb->mac_len; 85162306a36Sopenharmony_ci datalen = skb_tail_pointer(skb) - data; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci sg_init_one(&src, data, datalen); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci skcipher_request_set_sync_tfm(req, key->tfm0); 85662306a36Sopenharmony_ci skcipher_request_set_callback(req, 0, NULL, NULL); 85762306a36Sopenharmony_ci skcipher_request_set_crypt(req, &src, &src, datalen, iv); 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci err = crypto_skcipher_decrypt(req); 86062306a36Sopenharmony_ci skcipher_request_zero(req); 86162306a36Sopenharmony_ci return err; 86262306a36Sopenharmony_ci} 86362306a36Sopenharmony_ci 86462306a36Sopenharmony_cistatic int 86562306a36Sopenharmony_cillsec_do_decrypt_auth(struct sk_buff *skb, const struct mac802154_llsec *sec, 86662306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 86762306a36Sopenharmony_ci struct mac802154_llsec_key *key, __le64 dev_addr) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci u8 iv[16]; 87062306a36Sopenharmony_ci unsigned char *data; 87162306a36Sopenharmony_ci int authlen, datalen, assoclen, rc; 87262306a36Sopenharmony_ci struct scatterlist sg; 87362306a36Sopenharmony_ci struct aead_request *req; 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_ci authlen = ieee802154_sechdr_authtag_len(&hdr->sec); 87662306a36Sopenharmony_ci llsec_geniv(iv, dev_addr, &hdr->sec); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci req = aead_request_alloc(llsec_tfm_by_len(key, authlen), GFP_ATOMIC); 87962306a36Sopenharmony_ci if (!req) 88062306a36Sopenharmony_ci return -ENOMEM; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci assoclen = skb->mac_len; 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci data = skb_mac_header(skb) + skb->mac_len; 88562306a36Sopenharmony_ci datalen = skb_tail_pointer(skb) - data; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci sg_init_one(&sg, skb_mac_header(skb), assoclen + datalen); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci if (!(hdr->sec.level & IEEE802154_SCF_SECLEVEL_ENC)) { 89062306a36Sopenharmony_ci assoclen += datalen - authlen; 89162306a36Sopenharmony_ci datalen = authlen; 89262306a36Sopenharmony_ci } 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci aead_request_set_callback(req, 0, NULL, NULL); 89562306a36Sopenharmony_ci aead_request_set_crypt(req, &sg, &sg, datalen, iv); 89662306a36Sopenharmony_ci aead_request_set_ad(req, assoclen); 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci rc = crypto_aead_decrypt(req); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci kfree_sensitive(req); 90162306a36Sopenharmony_ci skb_trim(skb, skb->len - authlen); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci return rc; 90462306a36Sopenharmony_ci} 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_cistatic int 90762306a36Sopenharmony_cillsec_do_decrypt(struct sk_buff *skb, const struct mac802154_llsec *sec, 90862306a36Sopenharmony_ci const struct ieee802154_hdr *hdr, 90962306a36Sopenharmony_ci struct mac802154_llsec_key *key, __le64 dev_addr) 91062306a36Sopenharmony_ci{ 91162306a36Sopenharmony_ci if (hdr->sec.level == IEEE802154_SCF_SECLEVEL_ENC) 91262306a36Sopenharmony_ci return llsec_do_decrypt_unauth(skb, sec, hdr, key, dev_addr); 91362306a36Sopenharmony_ci else 91462306a36Sopenharmony_ci return llsec_do_decrypt_auth(skb, sec, hdr, key, dev_addr); 91562306a36Sopenharmony_ci} 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_cistatic int 91862306a36Sopenharmony_cillsec_update_devkey_record(struct mac802154_llsec_device *dev, 91962306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *in_key) 92062306a36Sopenharmony_ci{ 92162306a36Sopenharmony_ci struct mac802154_llsec_device_key *devkey; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci devkey = llsec_devkey_find(dev, in_key); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci if (!devkey) { 92662306a36Sopenharmony_ci struct mac802154_llsec_device_key *next; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci next = kzalloc(sizeof(*devkey), GFP_ATOMIC); 92962306a36Sopenharmony_ci if (!next) 93062306a36Sopenharmony_ci return -ENOMEM; 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_ci next->devkey.key_id = *in_key; 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci spin_lock_bh(&dev->lock); 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci devkey = llsec_devkey_find(dev, in_key); 93762306a36Sopenharmony_ci if (!devkey) 93862306a36Sopenharmony_ci list_add_rcu(&next->devkey.list, &dev->dev.keys); 93962306a36Sopenharmony_ci else 94062306a36Sopenharmony_ci kfree_sensitive(next); 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci spin_unlock_bh(&dev->lock); 94362306a36Sopenharmony_ci } 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_ci return 0; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_cistatic int 94962306a36Sopenharmony_cillsec_update_devkey_info(struct mac802154_llsec_device *dev, 95062306a36Sopenharmony_ci const struct ieee802154_llsec_key_id *in_key, 95162306a36Sopenharmony_ci u32 frame_counter) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct mac802154_llsec_device_key *devkey = NULL; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RESTRICT) { 95662306a36Sopenharmony_ci devkey = llsec_devkey_find(dev, in_key); 95762306a36Sopenharmony_ci if (!devkey) 95862306a36Sopenharmony_ci return -ENOENT; 95962306a36Sopenharmony_ci } 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci if (dev->dev.key_mode == IEEE802154_LLSEC_DEVKEY_RECORD) { 96262306a36Sopenharmony_ci int rc = llsec_update_devkey_record(dev, in_key); 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci if (rc < 0) 96562306a36Sopenharmony_ci return rc; 96662306a36Sopenharmony_ci } 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci spin_lock_bh(&dev->lock); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci if ((!devkey && frame_counter < dev->dev.frame_counter) || 97162306a36Sopenharmony_ci (devkey && frame_counter < devkey->devkey.frame_counter)) { 97262306a36Sopenharmony_ci spin_unlock_bh(&dev->lock); 97362306a36Sopenharmony_ci return -EINVAL; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (devkey) 97762306a36Sopenharmony_ci devkey->devkey.frame_counter = frame_counter + 1; 97862306a36Sopenharmony_ci else 97962306a36Sopenharmony_ci dev->dev.frame_counter = frame_counter + 1; 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci spin_unlock_bh(&dev->lock); 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci return 0; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ciint mac802154_llsec_decrypt(struct mac802154_llsec *sec, struct sk_buff *skb) 98762306a36Sopenharmony_ci{ 98862306a36Sopenharmony_ci struct ieee802154_hdr hdr; 98962306a36Sopenharmony_ci struct mac802154_llsec_key *key; 99062306a36Sopenharmony_ci struct ieee802154_llsec_key_id key_id; 99162306a36Sopenharmony_ci struct mac802154_llsec_device *dev; 99262306a36Sopenharmony_ci struct ieee802154_llsec_seclevel seclevel; 99362306a36Sopenharmony_ci int err; 99462306a36Sopenharmony_ci __le64 dev_addr; 99562306a36Sopenharmony_ci u32 frame_ctr; 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci if (ieee802154_hdr_peek(skb, &hdr) < 0) 99862306a36Sopenharmony_ci return -EINVAL; 99962306a36Sopenharmony_ci if (!hdr.fc.security_enabled) 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci if (hdr.fc.version == 0) 100262306a36Sopenharmony_ci return -EINVAL; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci read_lock_bh(&sec->lock); 100562306a36Sopenharmony_ci if (!sec->params.enabled) { 100662306a36Sopenharmony_ci read_unlock_bh(&sec->lock); 100762306a36Sopenharmony_ci return -EINVAL; 100862306a36Sopenharmony_ci } 100962306a36Sopenharmony_ci read_unlock_bh(&sec->lock); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci rcu_read_lock(); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci key = llsec_lookup_key(sec, &hdr, &hdr.source, &key_id); 101462306a36Sopenharmony_ci if (!key) { 101562306a36Sopenharmony_ci err = -ENOKEY; 101662306a36Sopenharmony_ci goto fail; 101762306a36Sopenharmony_ci } 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci dev = llsec_lookup_dev(sec, &hdr.source); 102062306a36Sopenharmony_ci if (!dev) { 102162306a36Sopenharmony_ci err = -EINVAL; 102262306a36Sopenharmony_ci goto fail_dev; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci if (llsec_lookup_seclevel(sec, hdr.fc.type, 0, &seclevel) < 0) { 102662306a36Sopenharmony_ci err = -EINVAL; 102762306a36Sopenharmony_ci goto fail_dev; 102862306a36Sopenharmony_ci } 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (!(seclevel.sec_levels & BIT(hdr.sec.level)) && 103162306a36Sopenharmony_ci (hdr.sec.level == 0 && seclevel.device_override && 103262306a36Sopenharmony_ci !dev->dev.seclevel_exempt)) { 103362306a36Sopenharmony_ci err = -EINVAL; 103462306a36Sopenharmony_ci goto fail_dev; 103562306a36Sopenharmony_ci } 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci frame_ctr = le32_to_cpu(hdr.sec.frame_counter); 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (frame_ctr == 0xffffffff) { 104062306a36Sopenharmony_ci err = -EOVERFLOW; 104162306a36Sopenharmony_ci goto fail_dev; 104262306a36Sopenharmony_ci } 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci err = llsec_update_devkey_info(dev, &key_id, frame_ctr); 104562306a36Sopenharmony_ci if (err) 104662306a36Sopenharmony_ci goto fail_dev; 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci dev_addr = dev->dev.hwaddr; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci rcu_read_unlock(); 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci err = llsec_do_decrypt(skb, sec, &hdr, key, dev_addr); 105362306a36Sopenharmony_ci llsec_key_put(key); 105462306a36Sopenharmony_ci return err; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cifail_dev: 105762306a36Sopenharmony_ci llsec_key_put(key); 105862306a36Sopenharmony_cifail: 105962306a36Sopenharmony_ci rcu_read_unlock(); 106062306a36Sopenharmony_ci return err; 106162306a36Sopenharmony_ci} 1062