/* * Copyright (c) 2017 Intel Corporation * Copyright (c) 2020 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #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" #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_KEYS) #define LOG_MODULE_NAME bt_mesh_app_keys #include "common/log.h" /* 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 itselve, 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; uint8_t val[2][16]; } __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; uint8_t val[16]; } 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) { BT_ERR("Failed to clear AppKeyIndex 0x%03x", app_idx); } else { BT_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) { BT_WARN("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, 16); memcpy(key.val[1], app->keys[1].val, 16); err = settings_save_one(path, &key, sizeof(key)); if (err) { BT_ERR("Failed to store AppKey %s value", log_strdup(path)); } else { BT_DBG("Stored AppKey %s value", log_strdup(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; BT_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) { BT_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; (void)memset(app->keys, 0, sizeof(app->keys)); } static void app_key_revoke(struct app_key *app) { if (!app->updated) { return; } 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; BT_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 (memcmp(key, app->keys[0].val, 16)) { return STATUS_IDX_ALREADY_STORED; } return STATUS_SUCCESS; } if (bt_mesh_app_id(key, &app->keys[0].id)) { return STATUS_CANNOT_SET; } BT_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; memcpy(app->keys[0].val, key, 16); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { BT_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; BT_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 (memcmp(app->keys[1].val, key, 16)) { return STATUS_IDX_ALREADY_STORED; } return STATUS_SUCCESS; } if (bt_mesh_app_id(key, &app->keys[1].id)) { return STATUS_CANNOT_UPDATE; } BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, app->keys[1].id); app->updated = true; memcpy(app->keys[1].val, key, 16); if (IS_ENABLED(CONFIG_BT_SETTINGS)) { BT_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; BT_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; } int bt_mesh_app_key_set(uint16_t app_idx, uint16_t net_idx, const uint8_t old_key[16], const uint8_t new_key[16]) { struct app_key *app; app = app_key_alloc(app_idx); if (!app) { return -ENOMEM; } if (app->app_idx == app_idx) { return 0; } BT_DBG("AppIdx 0x%04x AID 0x%02x", app_idx, app->keys[0].id); memcpy(app->keys[0].val, old_key, 16); if (bt_mesh_app_id(old_key, &app->keys[0].id)) { return -EIO; } if (new_key) { memcpy(app->keys[1].val, new_key, 16); if (bt_mesh_app_id(new_key, &app->keys[1].id)) { 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 uint8_t *app_key[16], 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) { BT_WARN("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)) { BT_WARN("No DevKey for 0x%04x", ctx->addr); return -EINVAL; } node = bt_mesh_cdb_node_get(ctx->addr); if (!node) { BT_WARN("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) { BT_WARN("Unknown AppKey 0x%03x", ctx->app_idx); return -EINVAL; } *sub = bt_mesh_subnet_get(app->net_idx); if (!*sub) { BT_WARN("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 uint8_t key[16], 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; } } 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(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; uint16_t app_idx; int err; if (!name) { BT_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; } err = bt_mesh_app_key_set(app_idx, key.net_idx, key.val[0], key.updated ? key.val[1] : NULL); if (err) { BT_ERR("Failed to set \'app-key\'"); return err; } BT_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; } if (update->clear) { clear_app_key(update->key_idx); } else { store_app_key(update->key_idx); } update->valid = 0U; } }