A mesh key type has been added to be able to choose the different key representation for different security libraries. The type as well as some functionality related to Mesh key management has been added as a public API. If tynicrypt is chosen then keys have representation as 16 bytes array. If mbedTLS with PSA is used then keys are the PSA key id. Raw value is not kept within BLE Mesh stack for mbedTLS. Keys are imported into the security library and key ids are gotten back. This refactoring has been done for the network(including all derivated keys), application, device, and session keys. Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
725 lines
15 KiB
C
725 lines
15 KiB
C
/*
|
|
* 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 "adv.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 &&
|
|
!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;
|
|
}
|
|
}
|
|
|
|
/** Bluetooth Mesh Specification v1.0.1, section 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) {
|
|
/* Bluetooth Mesh Specification v1.1.0, section 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;
|
|
}
|
|
|
|
update->valid = 0U;
|
|
|
|
if (update->clear) {
|
|
clear_app_key(update->key_idx);
|
|
} else {
|
|
store_app_key(update->key_idx);
|
|
}
|
|
}
|
|
}
|