Bluetooth: Mesh: Isolate cryptographic material

This is a major refactoring of the handling of the cryptographic
material of both the network and transport layers. The aim is to
encapsulate the key object manipulation, and improve overall modularity.

Pulls Applications and Subnets out of the bt_mesh and into separate
modules, with static storage types on the data. This has several
side-effects:
- The Config Server no longer operates directly on the bt_mesh.subs and
  bt_mesh.apps lists, but goes through a public configuration interface,
  following the pattern set in #27908.
- All iteration through the keys is done through iteration APIs
- Key resolution on RX and TX is centralized.
- Changes to the keys triggers events the other modules can register
  handlers for.
- Friendship credentials are stored in the lpn and friend structures.

Part of #27842.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2020-08-25 11:03:42 +02:00 committed by Johan Hedberg
commit eca0141152
30 changed files with 2611 additions and 2100 deletions

View file

@ -0,0 +1,498 @@
/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <string.h>
#include <stdlib.h>
#include <bluetooth/mesh.h>
#include <bluetooth/conn.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"
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_KEYS)
#define LOG_MODULE_NAME bt_mesh_app_keys
#include "common/log.h"
static struct bt_mesh_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 void app_key_evt(struct bt_mesh_app_key *app, enum bt_mesh_key_evt evt)
{
Z_STRUCT_SECTION_FOREACH(bt_mesh_app_key_cb, cb) {
cb->evt_handler(app->app_idx, app->net_idx, evt);
}
}
static struct bt_mesh_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 struct bt_mesh_app_key *app_key_alloc(uint16_t app_idx)
{
struct bt_mesh_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 bt_mesh_app_key *app)
{
BT_DBG("AppIdx 0x%03x", app->app_idx);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_clear_app_key(app->app_idx);
}
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 bt_mesh_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)) {
bt_mesh_store_app_key(app->app_idx);
}
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 bt_mesh_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_BINDING;
}
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");
bt_mesh_store_app_key(app->app_idx);
}
app_key_evt(app, BT_MESH_KEY_ADDED);
return STATUS_SUCCESS;
}
struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx)
{
struct bt_mesh_app_key *app;
app = app_get(app_idx);
if (app) {
return app;
}
return NULL;
}
uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16])
{
struct bt_mesh_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");
bt_mesh_store_app_key(app->app_idx);
}
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 bt_mesh_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 bt_mesh_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 bt_mesh_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 bt_mesh_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_elem_find(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 bt_mesh_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 bt_mesh_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 bt_mesh_app_key *app = &apps[i];
if (app->app_idx != BT_MESH_KEY_UNUSED) {
app_key_del(app);
}
}
}