zephyr/subsys/bluetooth/mesh/app_keys.c

724 lines
15 KiB
C
Raw Normal View History

/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <zephyr/bluetooth/mesh.h>
#include <zephyr/bluetooth/conn.h>
#include <zephyr/sys/iterable_sections.h>
#include "mesh.h"
#include "net.h"
#include "app_keys.h"
#include "rpl.h"
#include "settings.h"
#include "crypto.h"
#include "proxy.h"
#include "friend.h"
#include "foundation.h"
#include "access.h"
#include "common/bt_str.h"
#define LOG_LEVEL CONFIG_BT_MESH_KEYS_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(bt_mesh_app_keys);
/* Tracking of what storage changes are pending for App Keys. We track this in
* a separate array here instead of within the respective bt_mesh_app_key
* struct itself, since once a key gets deleted its struct becomes invalid
* and may be reused for other keys.
*/
struct app_key_update {
uint16_t key_idx:12, /* AppKey Index */
valid:1, /* 1 if this entry is valid, 0 if not */
clear:1; /* 1 if key needs clearing, 0 if storing */
};
/* AppKey information for persistent storage. */
struct app_key_val {
uint16_t net_idx;
bool updated;
struct bt_mesh_key val[2];
} __packed;
/** Mesh Application Key. */
struct app_key {
uint16_t net_idx;
uint16_t app_idx;
bool updated;
struct bt_mesh_app_cred {
uint8_t id;
struct bt_mesh_key val;
} keys[2];
};
static struct app_key_update app_key_updates[CONFIG_BT_MESH_APP_KEY_COUNT];
static struct app_key apps[CONFIG_BT_MESH_APP_KEY_COUNT] = {
[0 ... (CONFIG_BT_MESH_APP_KEY_COUNT - 1)] = {
.app_idx = BT_MESH_KEY_UNUSED,
.net_idx = BT_MESH_KEY_UNUSED,
}
};
static struct app_key *app_get(uint16_t app_idx)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
if (apps[i].app_idx == app_idx) {
return &apps[i];
}
}
return NULL;
}
static void clear_app_key(uint16_t app_idx)
{
char path[20];
int err;
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
err = settings_delete(path);
if (err) {
LOG_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx);
} else {
LOG_DBG("Cleared AppKeyIndex 0x%03x", app_idx);
}
}
static void store_app_key(uint16_t app_idx)
{
const struct app_key *app;
struct app_key_val key;
char path[20];
int err;
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
app = app_get(app_idx);
if (!app) {
LOG_WRN("ApKeyIndex 0x%03x not found", app_idx);
return;
}
key.net_idx = app->net_idx,
key.updated = app->updated,
memcpy(&key.val[0], &app->keys[0].val, sizeof(struct bt_mesh_key));
memcpy(&key.val[1], &app->keys[1].val, sizeof(struct bt_mesh_key));
err = settings_save_one(path, &key, sizeof(key));
if (err) {
LOG_ERR("Failed to store AppKey %s value", path);
} else {
LOG_DBG("Stored AppKey %s value", path);
}
}
static struct app_key_update *app_key_update_find(uint16_t key_idx,
struct app_key_update **free_slot)
{
struct app_key_update *match;
int i;
match = NULL;
*free_slot = NULL;
for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
struct app_key_update *update = &app_key_updates[i];
if (!update->valid) {
*free_slot = update;
continue;
}
if (update->key_idx == key_idx) {
match = update;
}
}
return match;
}
static void update_app_key_settings(uint16_t app_idx, bool store)
{
struct app_key_update *update, *free_slot;
uint8_t clear = store ? 0U : 1U;
LOG_DBG("AppKeyIndex 0x%03x", app_idx);
update = app_key_update_find(app_idx, &free_slot);
if (update) {
update->clear = clear;
bt_mesh_settings_store_schedule(
BT_MESH_SETTINGS_APP_KEYS_PENDING);
return;
}
if (!free_slot) {
if (store) {
store_app_key(app_idx);
} else {
clear_app_key(app_idx);
}
return;
}
free_slot->valid = 1U;
free_slot->key_idx = app_idx;
free_slot->clear = clear;
bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_APP_KEYS_PENDING);
}
static void app_key_evt(struct app_key *app, enum bt_mesh_key_evt evt)
{
STRUCT_SECTION_FOREACH(bt_mesh_app_key_cb, cb) {
cb->evt_handler(app->app_idx, app->net_idx, evt);
}
}
static struct app_key *app_key_alloc(uint16_t app_idx)
{
struct app_key *app = NULL;
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
/* Check for already existing app_key */
if (apps[i].app_idx == app_idx) {
return &apps[i];
}
if (!app && apps[i].app_idx == BT_MESH_KEY_UNUSED) {
app = &apps[i];
}
}
return app;
}
static void app_key_del(struct app_key *app)
{
LOG_DBG("AppIdx 0x%03x", app->app_idx);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
update_app_key_settings(app->app_idx, false);
}
app_key_evt(app, BT_MESH_KEY_DELETED);
app->net_idx = BT_MESH_KEY_UNUSED;
app->app_idx = BT_MESH_KEY_UNUSED;
bt_mesh_key_destroy(&app->keys[0].val);
bt_mesh_key_destroy(&app->keys[1].val);
memset(app->keys, 0, sizeof(app->keys));
}
static void app_key_revoke(struct app_key *app)
{
if (!app->updated) {
return;
}
bt_mesh_key_destroy(&app->keys[0].val);
memcpy(&app->keys[0], &app->keys[1], sizeof(app->keys[0]));
memset(&app->keys[1], 0, sizeof(app->keys[1]));
app->updated = false;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_REVOKED);
}
uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16])
{
struct app_key *app;
LOG_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx, bt_hex(key, 16));
if (!bt_mesh_subnet_get(net_idx)) {
return STATUS_INVALID_NETKEY;
}
app = app_key_alloc(app_idx);
if (!app) {
return STATUS_INSUFF_RESOURCES;
}
if (app->app_idx == app_idx) {
if (app->net_idx != net_idx) {
return STATUS_INVALID_NETKEY;
}
if (bt_mesh_key_compare(key, &app->keys[0].val)) {
return STATUS_IDX_ALREADY_STORED;
}
return STATUS_SUCCESS;
}
if (bt_mesh_app_id(key, &app->keys[0].id)) {
return STATUS_CANNOT_SET;
}
LOG_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
app->net_idx = net_idx;
app->app_idx = app_idx;
app->updated = false;
if (bt_mesh_key_import(BT_MESH_KEY_TYPE_APP, key, &app->keys[0].val)) {
LOG_ERR("Unable to import application key");
return STATUS_CANNOT_SET;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_DBG("Storing AppKey persistently");
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_ADDED);
return STATUS_SUCCESS;
}
uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16])
{
struct app_key *app;
struct bt_mesh_subnet *sub;
LOG_DBG("net_idx 0x%04x app_idx %04x val %s", net_idx, app_idx, bt_hex(key, 16));
app = app_get(app_idx);
if (!app) {
return STATUS_INVALID_APPKEY;
}
if (net_idx != BT_MESH_KEY_UNUSED && app->net_idx != net_idx) {
return STATUS_INVALID_BINDING;
}
sub = bt_mesh_subnet_get(app->net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
/* The AppKey Update message shall generate an error when node
* is in normal operation, Phase 2, or Phase 3 or in Phase 1
* when the AppKey Update message on a valid AppKeyIndex when
* the AppKey value is different.
*/
if (sub->kr_phase != BT_MESH_KR_PHASE_1) {
return STATUS_CANNOT_UPDATE;
}
if (app->updated) {
if (bt_mesh_key_compare(key, &app->keys[1].val)) {
return STATUS_IDX_ALREADY_STORED;
}
return STATUS_SUCCESS;
}
if (bt_mesh_app_id(key, &app->keys[1].id)) {
return STATUS_CANNOT_UPDATE;
}
LOG_DBG("app_idx 0x%04x AID 0x%02x", app_idx, app->keys[1].id);
app->updated = true;
if (bt_mesh_key_import(BT_MESH_KEY_TYPE_APP, key, &app->keys[1].val)) {
LOG_ERR("Unable to import application key");
return STATUS_CANNOT_UPDATE;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
LOG_DBG("Storing AppKey persistently");
update_app_key_settings(app->app_idx, true);
}
app_key_evt(app, BT_MESH_KEY_UPDATED);
return STATUS_SUCCESS;
}
uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx)
{
struct app_key *app;
LOG_DBG("AppIdx 0x%03x", app_idx);
if (net_idx != BT_MESH_KEY_UNUSED && !bt_mesh_subnet_get(net_idx)) {
return STATUS_INVALID_NETKEY;
}
app = app_get(app_idx);
if (!app) {
/* This could be a retry of a previous attempt that had its
* response lost, so pretend that it was a success.
*/
return STATUS_SUCCESS;
}
if (net_idx != BT_MESH_KEY_UNUSED && net_idx != app->net_idx) {
return STATUS_INVALID_BINDING;
}
app_key_del(app);
return STATUS_SUCCESS;
}
static int app_id_set(struct app_key *app, int key_idx, const struct bt_mesh_key *key)
{
uint8_t raw_key[16];
int err;
err = bt_mesh_key_export(raw_key, key);
if (err) {
return err;
}
err = bt_mesh_app_id(raw_key, &app->keys[key_idx].id);
if (err) {
return err;
}
bt_mesh_key_assign(&app->keys[key_idx].val, key);
return 0;
}
int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx,
const struct bt_mesh_key *old_key, const struct bt_mesh_key *new_key)
{
struct app_key *app;
app = app_key_alloc(app_idx);
if (!app) {
return -ENOMEM;
}
if (app->app_idx == app_idx) {
return 0;
}
LOG_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id);
if (app_id_set(app, 0, old_key)) {
return -EIO;
}
if (new_key != NULL && app_id_set(app, 1, new_key)) {
return -EIO;
}
app->net_idx = net_idx;
app->app_idx = app_idx;
app->updated = !!new_key;
return 0;
}
bool bt_mesh_app_key_exists(uint16_t app_idx)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
if (apps[i].app_idx == app_idx) {
return true;
}
}
return false;
}
ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
off_t skip)
{
size_t count = 0;
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (net_idx != BT_MESH_KEY_ANY && app->net_idx != net_idx) {
continue;
}
if (skip) {
skip--;
continue;
}
if (count >= max) {
return -ENOMEM;
}
app_idxs[count++] = app->app_idx;
}
return count;
}
int bt_mesh_keys_resolve(struct bt_mesh_msg_ctx *ctx,
struct bt_mesh_subnet **sub,
const struct bt_mesh_key **app_key, uint8_t *aid)
{
struct app_key *app = NULL;
if (BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
/* With device keys, the application has to decide which subnet
* to send on.
*/
*sub = bt_mesh_subnet_get(ctx->net_idx);
if (!*sub) {
LOG_WRN("Unknown NetKey 0x%03x", ctx->net_idx);
return -EINVAL;
}
if (ctx->app_idx == BT_MESH_KEY_DEV_REMOTE &&
Bluetooth: Mesh: Added support for application access to mesh messages Added bt_mesh_msg_send() which can be used by the application to directly send model layer messages without local instantiation of related models. Also added bt_mesh_msg_cb_set() which allows the application to recieve mesh model layer messages without local instantiation of related models. Added bt_mesh_has_addr() which returns a bool. For unicast addresses, this returns whether or not bt_mesh_elem_find() was successfull. If the above mentioned bt_mesh_msg_cb_set() has been used by the application to set a message callback, this returns true so that the stack attempts to push every model message up to the application via the callback. If no callback has been set, group addresses are searched to see if the stack should pass the message up the stack to an instantiated model. These changes allow applications that do not or can not instantiate models to interface with models in a mesh network. This is applicable to applications which act as a Bluetooth mesh gateway, sniffer, debugger, network monitoring, non-mesh relay/extender, etc. In app_keys.c friend.c net.c bt_mesh_elem_find() is used only to determine the existance of an address. The full return value of bt_mesh_elem_find() is unecessary and so was replaced by the above mentioned bt_mesh_has_addr() function in these instances. Simplified bt_mesh_elem_find() by removing the search through group address. Since the above mentioned bt_mesh_has_addr() function handles instances where group addresses must be searched, it was no longer necessary to preform this search in this function. Signed-off-by: Bud Wandinger <bud@budkoembedded.ca>
2021-05-30 03:18:10 +08:00
!bt_mesh_has_addr(ctx->addr)) {
struct bt_mesh_cdb_node *node;
if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
LOG_WRN("No DevKey for 0x%04x", ctx->addr);
return -EINVAL;
}
node = bt_mesh_cdb_node_get(ctx->addr);
if (!node) {
LOG_WRN("No DevKey for 0x%04x", ctx->addr);
return -EINVAL;
}
*app_key = &node->dev_key;
} else {
*app_key = &bt_mesh.dev_key;
}
*aid = 0;
return 0;
}
app = app_get(ctx->app_idx);
if (!app) {
LOG_WRN("Unknown AppKey 0x%03x", ctx->app_idx);
return -EINVAL;
}
*sub = bt_mesh_subnet_get(app->net_idx);
if (!*sub) {
LOG_WRN("Unknown NetKey 0x%03x", app->net_idx);
return -EINVAL;
}
if ((*sub)->kr_phase == BT_MESH_KR_PHASE_2 && app->updated) {
*aid = app->keys[1].id;
*app_key = &app->keys[1].val;
} else {
*aid = app->keys[0].id;
*app_key = &app->keys[0].val;
}
return 0;
}
uint16_t bt_mesh_app_key_find(bool dev_key, uint8_t aid,
struct bt_mesh_net_rx *rx,
int (*cb)(struct bt_mesh_net_rx *rx,
const struct bt_mesh_key *key, void *cb_data),
void *cb_data)
{
int err, i;
if (dev_key) {
/* Attempt remote dev key first, as that is only available for
* provisioner devices, which normally don't interact with nodes
* that know their local dev key.
*/
if (IS_ENABLED(CONFIG_BT_MESH_CDB) &&
rx->net_if != BT_MESH_NET_IF_LOCAL) {
struct bt_mesh_cdb_node *node;
node = bt_mesh_cdb_node_get(rx->ctx.addr);
if (node && !cb(rx, &node->dev_key, cb_data)) {
return BT_MESH_KEY_DEV_REMOTE;
}
}
/** MshPRTv1.1: 3.4.3:
* The Device key is only valid for unicast addresses.
*/
if (BT_MESH_ADDR_IS_UNICAST(rx->ctx.recv_dst)) {
err = cb(rx, &bt_mesh.dev_key, cb_data);
if (!err) {
return BT_MESH_KEY_DEV_LOCAL;
}
#if defined(CONFIG_BT_MESH_RPR_SRV)
if (atomic_test_bit(bt_mesh.flags, BT_MESH_DEVKEY_CAND)) {
err = cb(rx, &bt_mesh.dev_key_cand, cb_data);
if (!err) {
/* MshPRTv1.1: 3.6.4.2:
* If a message is successfully decrypted using the device
* key candidate, the device key candidate should
* permanently replace the original devkey.
*/
bt_mesh_dev_key_cand_activate();
return BT_MESH_KEY_DEV_LOCAL;
}
}
#endif
}
return BT_MESH_KEY_UNUSED;
}
for (i = 0; i < ARRAY_SIZE(apps); i++) {
const struct app_key *app = &apps[i];
const struct bt_mesh_app_cred *cred;
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (app->net_idx != rx->sub->net_idx) {
continue;
}
if (rx->new_key && app->updated) {
cred = &app->keys[1];
} else {
cred = &app->keys[0];
}
if (cred->id != aid) {
continue;
}
err = cb(rx, &cred->val, cb_data);
if (err) {
continue;
}
return app->app_idx;
}
return BT_MESH_KEY_UNUSED;
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_UPDATED || evt == BT_MESH_KEY_ADDED) {
return;
}
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (app->net_idx != sub->net_idx) {
continue;
}
if (evt == BT_MESH_KEY_DELETED) {
app_key_del(app);
} else if (evt == BT_MESH_KEY_REVOKED) {
app_key_revoke(app);
} else if (evt == BT_MESH_KEY_SWAPPED && app->updated) {
app_key_evt(app, BT_MESH_KEY_SWAPPED);
}
}
}
BT_MESH_SUBNET_CB_DEFINE(app_keys) = {
.evt_handler = subnet_evt,
};
void bt_mesh_app_keys_reset(void)
{
for (int i = 0; i < ARRAY_SIZE(apps); i++) {
struct app_key *app = &apps[i];
if (app->app_idx != BT_MESH_KEY_UNUSED) {
app_key_del(app);
}
}
}
static int app_key_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct app_key_val key;
struct bt_mesh_key val[2];
uint16_t app_idx;
int err;
if (!name) {
LOG_ERR("Insufficient number of arguments");
return -ENOENT;
}
app_idx = strtol(name, NULL, 16);
if (!len_rd) {
return 0;
}
err = bt_mesh_settings_set(read_cb, cb_arg, &key, sizeof(key));
if (err < 0) {
return -EINVAL;
}
/* One extra copying since key.val array is from packed structure
* and might be unaligned.
*/
memcpy(val, key.val, sizeof(key.val));
err = bt_mesh_app_key_set(app_idx, key.net_idx, &val[0],
key.updated ? &val[1] : NULL);
if (err) {
LOG_ERR("Failed to set \'app-key\'");
return err;
}
LOG_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
return 0;
}
BT_MESH_SETTINGS_DEFINE(app, "AppKey", app_key_set);
void bt_mesh_app_key_pending_store(void)
{
int i;
for (i = 0; i < ARRAY_SIZE(app_key_updates); i++) {
struct app_key_update *update = &app_key_updates[i];
if (!update->valid) {
continue;
}
Bluetooth: Mesh: Invalidate pending entries before calling settings API If, while storing app or net key, or changing cdb, we receive another mesh message that affects setting of the same key that the mesh currently stores, the new change won't be stored. The settings subsystem API has rescheduling point inside. The mesh settings are stored in the system workqueue and by default mesh messages are processed from BT RX thread which has higher priority than the system workqueue. When the case above happens, a new change will be written to the same cache entry. But this cache entry will be invalidated after leaving settings API, which in turns will invalidate the new change: - Receive Config AppKey Add message cfg_srv.c -> app_keys.c: bt_mesh_app_key_add() app_keys.c: update_app_key_settings() app_keys.c: bt_mesh_settings_store_schedule() - store_pending() in settings.c is scheduled settings.c -> app_keys.c: bt_mesh_app_key_pending_store() app_keys.c: store_app_key() called to store new app key app_keys.c: settings_save_one() calls flash driver and sleeps - Receive Config AppKey Delete message while in sleep, which wakes up BT RX thread before returning to the system workqueue cfg_srv.c -> app_keys.c: bt_mesh_app_key_del() app_keys.c: update_app_key_settings() app_keys.c: app_key_update_find() finds entry and returns it app_keys.c: update->clear = 1 app_keys.c: bt_mesh_settings_store_schedule() - returning back to bt_mesh_app_key_pending_store() from settings_save_one() app_keys.c: update->valid = 0 - the key won't be deleted next time store_pending() is scheduled. This change moves entry invalidation before calling settings API so that after returning from settings API, a new change won't be unintentionally invalidated. Signed-off-by: Pavel Vasilyev <pavel.vasilyev@nordicsemi.no>
2023-01-17 17:59:35 +08:00
update->valid = 0U;
if (update->clear) {
clear_app_key(update->key_idx);
} else {
store_app_key(update->key_idx);
}
}
}