|  | /* | 
|  | * 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 && | 
|  | !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; | 
|  | } | 
|  |  | 
|  | update->valid = 0U; | 
|  |  | 
|  | if (update->clear) { | 
|  | clear_app_key(update->key_idx); | 
|  | } else { | 
|  | store_app_key(update->key_idx); | 
|  | } | 
|  | } | 
|  | } |