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

@ -22,5 +22,6 @@
#include <bluetooth/mesh/health_cli.h>
#include <bluetooth/mesh/proxy.h>
#include <bluetooth/mesh/cdb.h>
#include <bluetooth/mesh/cfg.h>
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_H_ */

View file

@ -41,6 +41,7 @@ extern "C" {
#define BT_MESH_ADDR_RELAYS 0xfffe
#define BT_MESH_KEY_UNUSED 0xffff
#define BT_MESH_KEY_ANY 0xffff
#define BT_MESH_KEY_DEV 0xfffe
#define BT_MESH_KEY_DEV_LOCAL BT_MESH_KEY_DEV
#define BT_MESH_KEY_DEV_REMOTE 0xfffd

View file

@ -0,0 +1,320 @@
/** @file
* @brief Bluetooth Mesh Runtime Configuration APIs.
*/
/*
* Copyright (c) 2020 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_CFG_H_
#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_CFG_H_
#include <stdbool.h>
#include <stddef.h>
#include <sys/types.h>
/**
* @brief Bluetooth Mesh Runtime Configuration API
* @defgroup bt_mesh_cfg Bluetooth Mesh Runtime Configuration
* @ingroup bt_mesh
* @{
*/
#ifdef __cplusplus
extern "C" {
#endif
/** Bluetooth Mesh Feature states */
enum bt_mesh_feat_state {
/** Feature is supported, but disabled. */
BT_MESH_FEATURE_DISABLED,
/** Feature is supported and enabled. */
BT_MESH_FEATURE_ENABLED,
/** Feature is not supported, and cannot be enabled. */
BT_MESH_FEATURE_NOT_SUPPORTED,
};
/**
* @brief Bluetooth Mesh Subnet Configuration
* @defgroup bt_mesh_cfg_subnet Bluetooth Mesh Subnet Configuration
* @{
*/
/** @brief Add a Subnet.
*
* Adds a subnet with the given network index and network key to the list of
* known Subnets. All messages sent on the given Subnet will be processed by
* this node, and the node may send and receive Network Beacons on the given
* Subnet.
*
* @param net_idx Network index.
* @param key Root network key of the Subnet. All other keys are derived
* from this.
*
* @retval STATUS_SUCCESS The Subnet was successfully added.
* @retval STATUS_INSUFF_RESOURCES No room for this Subnet.
* @retval STATUS_UNSPECIFIED The Subnet couldn't be created for some reason.
*/
uint8_t bt_mesh_subnet_add(uint16_t net_idx, const uint8_t key[16]);
/** @brief Update the given Subnet.
*
* Starts the Key Refresh procedure for this Subnet by adding a second set of
* encryption keys. The Subnet will continue sending with the old key (but
* receiving messages using both) until the Subnet enters Key Refresh phase 2.
*
* This allows a network configurator to replace old network and application
* keys for the entire network, effectively removing access for all nodes that
* aren't given the new keys.
*
* @param net_idx Network index.
* @param key New root network key of the Subnet.
*
* @retval STATUS_SUCCESS The Subnet was updated with a second key.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_IDX_ALREADY_STORED The @c key value is the same as the
* current key.
* @retval STATUS_CANNOT_UPDATE The Subnet cannot be updated for some reason.
*/
uint8_t bt_mesh_subnet_update(uint16_t net_idx, const uint8_t key[16]);
/** @brief Delete a Subnet.
*
* Removes the Subnet with the given network index from the node. The node will
* stop sending Network Beacons with the given Subnet, and can no longer
* process messages on this Subnet.
*
* All Applications bound to this Subnet are also deleted.
*
* @param net_idx Network index.
*
* @retval STATUS_SUCCESS The Subnet was deleted.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
*/
uint8_t bt_mesh_subnet_del(uint16_t net_idx);
/** @brief Check whether a Subnet is known.
*
* @param net_idx Network index
*
* @return true if a Subnet with the given index exists, false otherwise.
*/
bool bt_mesh_subnet_exists(uint16_t net_idx);
/** @brief Set the Subnet's Key Refresh phase.
*
* The Key Refresh procedure is started by updating the Subnet keys through
* @ref bt_mesh_subnet_update. This puts the Subnet in Key Refresh Phase 1.
* Once all nodes have received the new Subnet key, Key Refresh Phase 2 can be
* activated through this function to start transmitting with the new network
* key. Finally, to revoke the old key, set the Key Refresh Phase to 3. This
* removes the old keys from the node, and returns the Subnet back to normal
* single-key operation with the new key set.
*
* @param net_idx Network index.
* @param phase Pointer to the new Key Refresh phase. Will return the actual
* Key Refresh phase after updating.
*
* @retval STATUS_SUCCESS The Key Refresh phase of the Subnet was successfully
* changed.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_CANNOT_UPDATE The given phase change is invalid.
*/
uint8_t bt_mesh_subnet_kr_phase_set(uint16_t net_idx, uint8_t *phase);
/** @brief Get the Subnet's Key Refresh phase.
*
* @param net_idx Network index.
* @param phase Pointer to the Key Refresh variable to fill.
*
* @retval STATUS_SUCCESS Successfully populated the @c phase variable.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
*/
uint8_t bt_mesh_subnet_kr_phase_get(uint16_t net_idx, uint8_t *phase);
/** @brief Set the Node Identity state of the Subnet.
*
* The Node Identity state of a Subnet determines whether the Subnet advertises
* connectable Node Identity beacons for Proxy Clients to connect to.
* Once started, the Node Identity beacon runs for 60 seconds, or until it is
* stopped.
*
* This function serves the same purpose as @ref bt_mesh_proxy_identity_enable,
* but only acts on a single Subnet.
*
* GATT Proxy support must be enabled through
* @option{CONFIG_BT_MESH_GATT_PROXY}.
*
* @param net_idx Network index.
* @param node_id New Node Identity state, must be either @ref
* BT_MESH_FEATURE_ENABLED or @ref BT_MESH_FEATURE_DISABLED.
*
* @retval STATUS_SUCCESS Successfully set the Node Identity state of the
* Subnet.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_FEAT_NOT_SUPP The Node Identity feature is not supported.
* @retval STATUS_CANNOT_SET Couldn't set the Node Identity state.
*/
uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
enum bt_mesh_feat_state node_id);
/** @brief Get the Node Identity state of the Subnet.
*
* @param net_idx Network index.
* @param node_id Node Identity variable to fill.
*
* @retval STATUS_SUCCESS Successfully populated the @c node_id variable.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
*/
uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
enum bt_mesh_feat_state *node_id);
/** @brief Get a list of all known Subnet indexes.
*
* Builds a list of all known Subnet indexes in the @c net_idxs array.
* If the @c net_idxs array is smaller than the list of known Subnets, this
* function fills all available entries and returns @c -ENOMEM. In this
* case, the next @c max entries of the list can be read out by calling
* @code
* bt_mesh_subnets_get(list, max, max);
* @endcode
*
* Note that any changes to the Subnet list between calls to this function
* could change the order and number of entries in the list.
*
* @param net_idxs Array to fill.
* @param max Max number of indexes to return.
* @param skip Number of indexes to skip. Enables batched processing of the
* list.
*
* @return The number of indexes added to the @c net_idxs array, or @c -ENOMEM
* if the number of known Subnets exceeds the @c max parameter.
*/
ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip);
/**
* @}
*/
/**
* @brief Bluetooth Mesh Application Configuration
* @defgroup bt_mesh_cfg_app Bluetooth Mesh Application Configuration
* @{
*/
/** @brief Add an Application key.
*
* Adds the Application with the given index to the list of known applications.
* Allows the node to send and receive model messages encrypted with this
* Application key.
*
* Every Application is bound to a specific Subnet. The node must know the
* Subnet the Application is bound to before it can add the Application.
*
* @param app_idx Application index.
* @param net_idx Network index the Application is bound to.
* @param key Application key value.
*
* @retval STATUS_SUCCESS The Application was successfully added.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_INSUFF_RESOURCES There's no room for storing this
* Application.
* @retval STATUS_INVALID_BINDING This AppIdx is already bound to another
* Subnet.
* @retval STATUS_IDX_ALREADY_STORED This AppIdx is already stored with a
* different key value.
* @retval STATUS_CANNOT_SET Cannot set the Application key for some reason.
*/
uint8_t bt_mesh_app_key_add(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16]);
/** @brief Update an Application key.
*
* Update an Application with a second Application key, as part of the
* Key Refresh procedure of the bound Subnet. The node will continue
* transmitting with the old application key (but receiving on both) until the
* Subnet enters Key Refresh phase 2. Once the Subnet enters Key Refresh phase
* 3, the old application key will be deleted.
*
* @note The Application key can only be updated if the bound Subnet is in Key
* Refresh phase 1.
*
* @param app_idx Application index.
* @param net_idx Network index the Application is bound to, or
* @ref BT_MESH_KEY_ANY to skip the binding check.
* @param key New key value.
*
* @retval STATUS_SUCCESS The Application key was successfully updated.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_INVALID_BINDING This AppIdx is not bound to the given NetIdx.
* @retval STATUS_CANNOT_UPDATE The Application key cannot be updated for some
* reason.
* @retval STATUS_IDX_ALREADY_STORED This AppIdx is already updated with a
* different key value.
*/
uint8_t bt_mesh_app_key_update(uint16_t app_idx, uint16_t net_idx,
const uint8_t key[16]);
/** @brief Delete an Application key.
*
* All models bound to this application will remove this binding.
* All models publishing with this application will stop publishing.
*
* @param app_idx Application index.
* @param net_idx Network index.
*
* @retval STATUS_SUCCESS The Application key was successfully deleted.
* @retval STATUS_INVALID_NETKEY The NetIdx is unknown.
* @retval STATUS_INVALID_BINDING This AppIdx is not bound to the given NetIdx.
*/
uint8_t bt_mesh_app_key_del(uint16_t app_idx, uint16_t net_idx);
/** @brief Check if an Application key is known.
*
* @param app_idx Application index.
*
* @return true if the Application is known, false otherwise.
*/
bool bt_mesh_app_key_exists(uint16_t app_idx);
/** @brief Get a list of all known Application key indexes.
*
* Builds a list of all Application indexes for the given network index in the
* @c app_idxs array. If the @c app_idxs array cannot fit all bound
* Applications, this function fills all available entries and returns @c
* -ENOMEM. In this case, the next @c max entries of the list can be read out
* by calling
* @code
* bt_mesh_app_keys_get(net_idx, list, max, max);
* @endcode
*
* Note that any changes to the Application key list between calls to this
* function could change the order and number of entries in the list.
*
* @param net_idx Network Index to get the Applications of, or @ref
* BT_MESH_KEY_ANY to get all Applications.
* @param app_idxs Array to fill.
* @param max Max number of indexes to return.
* @param skip Number of indexes to skip. Enables batched processing of the
* list.
*
* @return The number of indexes added to the @c app_idxs array, or @c -ENOMEM
* if the number of known Applications exceeds the @c max parameter.
*/
ssize_t bt_mesh_app_keys_get(uint16_t net_idx, uint16_t app_idxs[], size_t max,
off_t skip);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_CFG_H_ */

View file

@ -102,6 +102,11 @@
Z_ITERABLE_SECTION_ROM(bt_gatt_service_static, 4)
#if defined(CONFIG_BT_MESH)
Z_ITERABLE_SECTION_ROM(bt_mesh_subnet_cb, 4)
Z_ITERABLE_SECTION_ROM(bt_mesh_app_key_cb, 4)
#endif
#if defined(CONFIG_EC_HOST_CMD)
Z_ITERABLE_SECTION_ROM(ec_host_cmd_handler, 4)
#endif

View file

@ -8,6 +8,8 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH
adv.c
beacon.c
net.c
subnet.c
app_keys.c
transport.c
rpl.c
crypto.c

View file

@ -642,6 +642,12 @@ config BT_MESH_DEBUG_CRYPTO
Use this option to enable cryptographic debug logs for the
Bluetooth Mesh functionality.
config BT_MESH_DEBUG_KEYS
bool "Key management debug"
help
Use this option to enable key management debug logs for the
Bluetooth Mesh functionality.
config BT_MESH_DEBUG_PROV
bool "Provisioning debug"
help

View file

@ -158,28 +158,17 @@ static int publish_retransmit(struct bt_mesh_model *mod)
{
NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
struct bt_mesh_model_pub *pub = mod->pub;
struct bt_mesh_app_key *key;
struct bt_mesh_msg_ctx ctx = {
.addr = pub->addr,
.send_ttl = pub->ttl,
.app_idx = pub->key,
};
struct bt_mesh_net_tx tx = {
.ctx = &ctx,
.src = bt_mesh_model_elem(mod)->addr,
.xmit = bt_mesh_net_transmit_get(),
.friend_cred = pub->cred,
};
key = bt_mesh_app_key_find(pub->key);
if (!key) {
return -EADDRNOTAVAIL;
}
tx.sub = bt_mesh_subnet_get(key->net_idx);
ctx.net_idx = key->net_idx;
ctx.app_idx = key->app_idx;
net_buf_simple_add_mem(&sdu, pub->msg->data, pub->msg->len);
pub->count--;
@ -695,24 +684,9 @@ int bt_mesh_model_send(struct bt_mesh_model *model,
struct net_buf_simple *msg,
const struct bt_mesh_send_cb *cb, void *cb_data)
{
struct bt_mesh_app_key *app_key;
if (!BT_MESH_IS_DEV_KEY(ctx->app_idx)) {
app_key = bt_mesh_app_key_find(ctx->app_idx);
if (!app_key) {
BT_ERR("Unknown app_idx 0x%04x", ctx->app_idx);
return -EINVAL;
}
ctx->net_idx = app_key->net_idx;
}
struct bt_mesh_net_tx tx = {
.sub = bt_mesh_subnet_get(ctx->net_idx),
.ctx = ctx,
.src = bt_mesh_model_elem(model)->addr,
.xmit = bt_mesh_net_transmit_get(),
.friend_cred = 0,
};
return model_send(model, &tx, false, msg, cb, cb_data);
@ -722,13 +696,15 @@ int bt_mesh_model_publish(struct bt_mesh_model *model)
{
NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_TX_SDU_MAX);
struct bt_mesh_model_pub *pub = model->pub;
struct bt_mesh_app_key *key;
struct bt_mesh_msg_ctx ctx = {
.addr = pub->addr,
.send_ttl = pub->ttl,
.send_rel = pub->send_rel,
.app_idx = pub->key,
};
struct bt_mesh_net_tx tx = {
.ctx = &ctx,
.src = bt_mesh_model_elem(model)->addr,
.xmit = bt_mesh_net_transmit_get(),
};
int err;
@ -742,11 +718,6 @@ int bt_mesh_model_publish(struct bt_mesh_model *model)
return -EADDRNOTAVAIL;
}
key = bt_mesh_app_key_find(pub->key);
if (!key) {
return -EADDRNOTAVAIL;
}
if (pub->msg->len + 4 > BT_MESH_TX_SDU_MAX) {
BT_ERR("Message does not fit maximum SDU size");
return -EMSGSIZE;
@ -759,14 +730,7 @@ int bt_mesh_model_publish(struct bt_mesh_model *model)
net_buf_simple_add_mem(&sdu, pub->msg->data, pub->msg->len);
ctx.addr = pub->addr;
ctx.send_ttl = pub->ttl;
ctx.send_rel = pub->send_rel;
ctx.net_idx = key->net_idx;
ctx.app_idx = key->app_idx;
tx.friend_cred = pub->cred;
tx.sub = bt_mesh_subnet_get(ctx.net_idx),
pub->count = BT_MESH_PUB_TRANSMIT_COUNT(pub->retransmit);

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);
}
}
}

View file

@ -0,0 +1,81 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_BLUETOOTH_MESH_APP_KEYS_H_
#define ZEPHYR_SUBSYS_BLUETOOTH_MESH_APP_KEYS_H_
#include <bluetooth/mesh.h>
#include "subnet.h"
/** Mesh Application. */
struct bt_mesh_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];
};
/** @brief Reset the app keys module. */
void bt_mesh_app_keys_reset(void);
/** @brief Get the application key with the given AppIdx.
*
* @param app_idx App index.
*
* @return The matching application, or NULL if the application isn't known.
*/
struct bt_mesh_app_key *bt_mesh_app_key_get(uint16_t app_idx);
/** @brief Initialize a new application key with the given parameters.
*
* @param app_idx AppIndex.
* @param net_idx NetIndex the application is bound to.
* @param old_key Current application key.
* @param new_key Updated application key, or NULL if not known.
*
* @return 0 on success, or (negative) error code on failure.
*/
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]);
/** @brief Resolve the message encryption keys, given a message context.
*
* Will use the @c ctx::app_idx and @c ctx::net_idx fields to find a pair of
* message encryption keys. If @c ctx::app_idx represents a device key, the
* @c ctx::net_idx will be used to determine the net key. Otherwise, the
* @c ctx::net_idx parameter will be ignored.
*
* @param ctx Message context.
* @param sub Subnet return parameter.
* @param app_key Application return parameter.
* @param aid Application ID return parameter.
*
* @return 0 on success, or (negative) error code on failure.
*/
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);
/** @brief Iterate through all matching application keys and call @c cb on each.
*
* @param dev_key Whether to return device keys.
* @param aid 7 bit application ID to match.
* @param rx RX structure to match against.
* @param cb Callback to call for every valid app key.
* @param cb_data Callback data to pass to the callback.
*
* @return The AppIdx that yielded a 0-return from the callback.
*/
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);
#endif /* ZEPHYR_SUBSYS_BLUETOOTH_MESH_APP_KEYS_H_ */

View file

@ -40,23 +40,9 @@
static struct k_delayed_work beacon_timer;
static struct bt_mesh_subnet *cache_check(uint8_t data[21])
static int cache_check(struct bt_mesh_subnet *sub, void *beacon_data)
{
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (!memcmp(sub->beacon_cache, data, 21)) {
return sub;
}
}
return NULL;
return !memcmp(sub->beacon_cache, beacon_data, 21);
}
static void cache_add(uint8_t data[21], struct bt_mesh_subnet *sub)
@ -81,11 +67,7 @@ void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE);
if (sub->kr_flag) {
keys = &sub->keys[1];
} else {
keys = &sub->keys[0];
}
keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
net_buf_simple_add_u8(buf, flags);
@ -107,44 +89,34 @@ void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
#define BEACON_THRESHOLD(sub) \
((10 * ((sub)->beacons_last + 1)) * MSEC_PER_SEC - (5 * MSEC_PER_SEC))
static int secure_beacon_send(void)
static int secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data)
{
static const struct bt_mesh_send_cb send_cb = {
.end = beacon_complete,
};
uint32_t now = k_uptime_get_32();
int i;
struct net_buf *buf;
uint32_t time_diff;
BT_DBG("");
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
struct net_buf *buf;
uint32_t time_diff;
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
time_diff = now - sub->beacon_sent;
if (time_diff < (600 * MSEC_PER_SEC) &&
time_diff < BEACON_THRESHOLD(sub)) {
continue;
}
buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT,
K_NO_WAIT);
if (!buf) {
BT_ERR("Unable to allocate beacon buffer");
return -ENOBUFS;
}
bt_mesh_beacon_create(sub, &buf->b);
bt_mesh_adv_send(buf, &send_cb, sub);
net_buf_unref(buf);
time_diff = now - sub->beacon_sent;
if (time_diff < (600 * MSEC_PER_SEC) &&
time_diff < BEACON_THRESHOLD(sub)) {
return 0;
}
buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, PROV_XMIT, K_NO_WAIT);
if (!buf) {
BT_ERR("Unable to allocate beacon buffer");
return -ENOMEM;
}
bt_mesh_beacon_create(sub, &buf->b);
bt_mesh_adv_send(buf, &send_cb, sub);
net_buf_unref(buf);
return 0;
}
@ -236,10 +208,15 @@ static void unprovisioned_beacon_recv(struct net_buf_simple *buf)
}
}
static void sub_update_beacon_observation(struct bt_mesh_subnet *sub)
{
sub->beacons_last = sub->beacons_cur;
sub->beacons_cur = 0U;
}
static void update_beacon_observation(void)
{
static bool first_half;
int i;
/* Observation period is 20 seconds, whereas the beacon timer
* runs every 10 seconds. We process what's happened during the
@ -250,16 +227,7 @@ static void update_beacon_observation(void)
return;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
sub->beacons_last = sub->beacons_cur;
sub->beacons_cur = 0U;
}
bt_mesh_subnet_foreach(sub_update_beacon_observation);
}
static void beacon_send(struct k_work *work)
@ -274,7 +242,7 @@ static void beacon_send(struct k_work *work)
if (bt_mesh_is_provisioned()) {
update_beacon_observation();
secure_beacon_send();
(void)bt_mesh_subnet_find(secure_beacon_send, NULL);
/* Only resubmit if beaconing is still enabled */
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED ||
@ -292,20 +260,62 @@ static void beacon_send(struct k_work *work)
}
}
struct beacon_params {
const uint8_t *net_id;
const uint8_t *auth;
uint32_t iv_index;
uint8_t flags;
bool new_key;
};
static bool auth_match(struct bt_mesh_subnet_keys *keys,
const struct beacon_params *params)
{
uint8_t net_auth[8];
if (memcmp(params->net_id, keys->net_id, 8)) {
return false;
}
bt_mesh_beacon_auth(keys->beacon, params->flags, keys->net_id,
params->iv_index, net_auth);
if (memcmp(params->auth, net_auth, 8)) {
BT_WARN("Authentication Value %s != %s",
bt_hex(params->auth, 8), bt_hex(net_auth, 8));
return false;
}
return true;
}
static int subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data)
{
struct beacon_params *params = cb_data;
for (int i = 0; i < ARRAY_SIZE(sub->keys); i++) {
if (sub->keys[i].valid && auth_match(&sub->keys[i], params)) {
params->new_key = (i > 0);
return true;
}
}
return false;
}
static void secure_beacon_recv(struct net_buf_simple *buf)
{
uint8_t *data, *net_id, *auth;
struct beacon_params params;
struct bt_mesh_subnet *sub;
uint32_t iv_index;
bool new_key, kr_change, iv_change;
uint8_t flags;
uint8_t *data;
if (buf->len < 21) {
BT_ERR("Too short secure beacon (len %u)", buf->len);
return;
}
sub = cache_check(buf->data);
sub = bt_mesh_subnet_find(cache_check, buf->data);
if (sub) {
/* We've seen this beacon before - just update the stats */
goto update_stats;
@ -314,33 +324,29 @@ static void secure_beacon_recv(struct net_buf_simple *buf)
/* So we can add to the cache if auth matches */
data = buf->data;
flags = net_buf_simple_pull_u8(buf);
net_id = net_buf_simple_pull_mem(buf, 8);
iv_index = net_buf_simple_pull_be32(buf);
auth = buf->data;
params.flags = net_buf_simple_pull_u8(buf);
params.net_id = net_buf_simple_pull_mem(buf, 8);
params.iv_index = net_buf_simple_pull_be32(buf);
params.auth = buf->data;
BT_DBG("flags 0x%02x id %s iv_index 0x%08x",
flags, bt_hex(net_id, 8), iv_index);
params.flags, bt_hex(params.net_id, 8), params.iv_index);
sub = bt_mesh_subnet_find(net_id, flags, iv_index, auth, &new_key);
sub = bt_mesh_subnet_find(subnet_by_id, &params);
if (!sub) {
BT_DBG("No subnet that matched beacon");
return;
}
if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !new_key) {
if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params.new_key) {
BT_WARN("Ignoring Phase 2 KR Update secured using old key");
return;
}
cache_add(data, sub);
kr_change = bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(flags), new_key);
if (kr_change) {
bt_mesh_net_beacon_update(sub);
/* Key Refresh without IV Update only impacts one subnet */
bt_mesh_net_sec_update(sub);
}
bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(params.flags),
params.new_key);
/* If we have NetKey0 accept initiation only from it */
if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
@ -350,20 +356,15 @@ static void secure_beacon_recv(struct net_buf_simple *buf)
}
BT_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x",
sub->net_idx, iv_index, bt_mesh.iv_index);
sub->net_idx, params.iv_index, bt_mesh.iv_index);
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) &&
(atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS) ==
BT_MESH_IV_UPDATE(flags))) {
BT_MESH_IV_UPDATE(params.flags))) {
bt_mesh_beacon_ivu_initiator(false);
}
iv_change = bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(flags));
if (iv_change) {
/* Update all subnets */
bt_mesh_net_sec_update(NULL);
}
bt_mesh_net_iv_update(params.iv_index, BT_MESH_IV_UPDATE(params.flags));
update_stats:
if (bt_mesh_beacon_get() == BT_MESH_BEACON_ENABLED &&
@ -399,6 +400,34 @@ void bt_mesh_beacon_recv(struct net_buf_simple *buf)
}
}
void bt_mesh_beacon_update(struct bt_mesh_subnet *sub)
{
uint8_t flags = bt_mesh_net_flags(sub);
struct bt_mesh_subnet_keys *keys;
int err;
keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
BT_DBG("NetIndex 0x%03x Using %s key", sub->net_idx,
SUBNET_KEY_TX_IDX(sub) ? "new" : "current");
BT_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index);
err = bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
bt_mesh.iv_index, sub->auth);
if (err) {
BT_ERR("Failed updating net beacon for 0x%03x", sub->net_idx);
}
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_ADDED || evt == BT_MESH_KEY_SWAPPED) {
bt_mesh_beacon_update(sub);
}
}
BT_MESH_SUBNET_CB_DEFINE(subnet_evt);
void bt_mesh_beacon_init(void)
{
k_delayed_work_init(&beacon_timer, beacon_send);
@ -415,27 +444,22 @@ void bt_mesh_beacon_ivu_initiator(bool enable)
}
}
static void subnet_beacon_enable(struct bt_mesh_subnet *sub)
{
sub->beacons_last = 0U;
sub->beacons_cur = 0U;
bt_mesh_beacon_update(sub);
}
void bt_mesh_beacon_enable(void)
{
int i;
if (!bt_mesh_is_provisioned()) {
k_work_submit(&beacon_timer.work);
return;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
sub->beacons_last = 0U;
sub->beacons_cur = 0U;
bt_mesh_net_beacon_update(sub);
}
bt_mesh_subnet_foreach(subnet_beacon_enable);
k_work_submit(&beacon_timer.work);
}

View file

@ -17,3 +17,4 @@ void bt_mesh_beacon_create(struct bt_mesh_subnet *sub,
struct net_buf_simple *buf);
void bt_mesh_beacon_init(void);
void bt_mesh_beacon_update(struct bt_mesh_subnet *sub);

View file

@ -179,22 +179,6 @@ static struct bt_mesh_model *get_model(struct bt_mesh_elem *elem,
}
}
static bool app_key_is_valid(uint16_t app_idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
if (key->net_idx != BT_MESH_KEY_UNUSED &&
key->app_idx == app_idx) {
return true;
}
}
return false;
}
static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr,
uint16_t app_idx, uint8_t cred_flag, uint8_t ttl, uint8_t period,
uint8_t retransmit, bool store)
@ -235,7 +219,7 @@ static uint8_t _mod_pub_set(struct bt_mesh_model *model, uint16_t pub_addr,
return STATUS_SUCCESS;
}
if (!bt_mesh_app_key_find(app_idx)) {
if (!bt_mesh_app_key_exists(app_idx)) {
return STATUS_INVALID_APPKEY;
}
@ -283,7 +267,7 @@ static uint8_t mod_bind(struct bt_mesh_model *model, uint16_t key_idx)
BT_DBG("model %p key_idx 0x%03x", model, key_idx);
if (!app_key_is_valid(key_idx)) {
if (!bt_mesh_app_key_exists(key_idx)) {
return STATUS_INVALID_APPKEY;
}
@ -315,7 +299,7 @@ static uint8_t mod_unbind(struct bt_mesh_model *model, uint16_t key_idx, bool st
BT_DBG("model %p key_idx 0x%03x store %u", model, key_idx, store);
if (!app_key_is_valid(key_idx)) {
if (!bt_mesh_app_key_exists(key_idx)) {
return STATUS_INVALID_APPKEY;
}
@ -339,115 +323,26 @@ static uint8_t mod_unbind(struct bt_mesh_model *model, uint16_t key_idx, bool st
return STATUS_SUCCESS;
}
struct bt_mesh_app_key *bt_mesh_app_key_alloc(uint16_t app_idx)
static void send_app_key_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
uint8_t status,
uint16_t app_idx, uint16_t net_idx)
{
int i;
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_STATUS, 4);
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS);
net_buf_simple_add_u8(&msg, status);
key_idx_pack(&msg, net_idx, app_idx);
if (key->net_idx == BT_MESH_KEY_UNUSED) {
return key;
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send App Key Status response");
}
return NULL;
}
static uint8_t app_key_set(uint16_t net_idx, uint16_t app_idx, const uint8_t val[16],
bool update)
{
struct bt_mesh_app_keys *keys;
struct bt_mesh_app_key *key;
struct bt_mesh_subnet *sub;
BT_DBG("net_idx 0x%04x app_idx %04x update %u val %s",
net_idx, app_idx, update, bt_hex(val, 16));
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
key = bt_mesh_app_key_find(app_idx);
if (update) {
if (!key) {
return STATUS_INVALID_APPKEY;
}
if (key->net_idx != net_idx) {
return STATUS_INVALID_BINDING;
}
keys = &key->keys[1];
/* 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 (key->updated) {
if (memcmp(keys->val, val, 16)) {
return STATUS_CANNOT_UPDATE;
} else {
return STATUS_SUCCESS;
}
}
key->updated = true;
} else {
if (key) {
if (key->net_idx == net_idx &&
!memcmp(key->keys[0].val, val, 16)) {
return STATUS_SUCCESS;
}
if (key->net_idx == net_idx) {
return STATUS_IDX_ALREADY_STORED;
} else {
return STATUS_INVALID_NETKEY;
}
}
key = bt_mesh_app_key_alloc(app_idx);
if (!key) {
return STATUS_INSUFF_RESOURCES;
}
keys = &key->keys[0];
}
if (bt_mesh_app_id(val, &keys->id)) {
if (update) {
key->updated = false;
}
return STATUS_STORAGE_FAIL;
}
BT_DBG("app_idx 0x%04x AID 0x%02x", app_idx, keys->id);
key->net_idx = net_idx;
key->app_idx = app_idx;
memcpy(keys->val, val, 16);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing AppKey persistently");
bt_mesh_store_app_key(key);
}
return STATUS_SUCCESS;
}
static void app_key_add(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_STATUS, 4);
uint16_t key_net_idx, key_app_idx;
uint8_t status;
@ -455,24 +350,15 @@ static void app_key_add(struct bt_mesh_model *model,
BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS);
status = bt_mesh_app_key_add(key_app_idx, key_net_idx, buf->data);
status = app_key_set(key_net_idx, key_app_idx, buf->data, false);
BT_DBG("status 0x%02x", status);
net_buf_simple_add_u8(&msg, status);
key_idx_pack(&msg, key_net_idx, key_app_idx);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send App Key Status response");
}
send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
static void app_key_update(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_STATUS, 4);
uint16_t key_net_idx, key_app_idx;
uint8_t status;
@ -480,93 +366,45 @@ static void app_key_update(struct bt_mesh_model *model,
BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS);
status = app_key_set(key_net_idx, key_app_idx, buf->data, true);
status = bt_mesh_app_key_update(key_app_idx, key_net_idx, buf->data);
BT_DBG("status 0x%02x", status);
net_buf_simple_add_u8(&msg, status);
key_idx_pack(&msg, key_net_idx, key_app_idx);
send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send App Key Status response");
static void mod_app_key_del(struct bt_mesh_model *mod,
struct bt_mesh_elem *elem, bool vnd, bool primary,
void *user_data)
{
uint16_t *app_idx = user_data;
mod_unbind(mod, *app_idx, true);
}
static void app_key_evt(uint16_t app_idx, uint16_t net_idx,
enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_DELETED) {
bt_mesh_model_foreach(&mod_app_key_del, &app_idx);
}
}
struct unbind_data {
uint16_t app_idx;
bool store;
};
static void _mod_unbind(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
struct unbind_data *data = user_data;
mod_unbind(mod, data->app_idx, data->store);
}
void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store)
{
struct unbind_data data = { .app_idx = key->app_idx, .store = store };
BT_DBG("AppIdx 0x%03x store %u", key->app_idx, store);
bt_mesh_model_foreach(_mod_unbind, &data);
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_clear_app_key(key);
}
key->net_idx = BT_MESH_KEY_UNUSED;
(void)memset(key->keys, 0, sizeof(key->keys));
}
BT_MESH_APP_KEY_CB_DEFINE(app_key_evt);
static void app_key_del(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_STATUS, 4);
uint16_t key_net_idx, key_app_idx;
struct bt_mesh_app_key *key;
uint8_t status;
key_idx_unpack(buf, &key_net_idx, &key_app_idx);
BT_DBG("AppIdx 0x%04x NetIdx 0x%04x", key_app_idx, key_net_idx);
if (!bt_mesh_subnet_get(key_net_idx)) {
status = STATUS_INVALID_NETKEY;
goto send_status;
}
status = bt_mesh_app_key_del(key_net_idx, key_net_idx);
key = bt_mesh_app_key_find(key_app_idx);
if (!key) {
/* Treat as success since the client might have missed a
* previous response and is resending the request.
*/
status = STATUS_SUCCESS;
goto send_status;
}
if (key->net_idx != key_net_idx) {
status = STATUS_INVALID_BINDING;
goto send_status;
}
bt_mesh_app_key_del(key, true);
status = STATUS_SUCCESS;
send_status:
bt_mesh_model_msg_init(&msg, OP_APP_KEY_STATUS);
net_buf_simple_add_u8(&msg, status);
key_idx_pack(&msg, key_net_idx, key_app_idx);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send App Key Status response");
}
send_app_key_status(model, ctx, status, key_app_idx, key_net_idx);
}
/* Index list length: 3 bytes for every pair and 2 bytes for an odd idx */
@ -578,8 +416,11 @@ static void app_key_get(struct bt_mesh_model *model,
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_APP_KEY_LIST,
3 + IDX_LEN(CONFIG_BT_MESH_APP_KEY_COUNT));
uint16_t get_idx, i, prev;
uint16_t app_idx[CONFIG_BT_MESH_APP_KEY_COUNT];
uint16_t get_idx;
uint8_t status;
ssize_t count;
int i;
get_idx = net_buf_simple_pull_le16(buf);
if (get_idx > 0xfff) {
@ -591,7 +432,7 @@ static void app_key_get(struct bt_mesh_model *model,
bt_mesh_model_msg_init(&msg, OP_APP_KEY_LIST);
if (!bt_mesh_subnet_get(get_idx)) {
if (!bt_mesh_subnet_exists(get_idx)) {
status = STATUS_INVALID_NETKEY;
} else {
status = STATUS_SUCCESS;
@ -604,25 +445,17 @@ static void app_key_get(struct bt_mesh_model *model,
goto send_status;
}
prev = BT_MESH_KEY_UNUSED;
for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
if (key->net_idx != get_idx) {
continue;
}
if (prev == BT_MESH_KEY_UNUSED) {
prev = key->app_idx;
continue;
}
key_idx_pack(&msg, prev, key->app_idx);
prev = BT_MESH_KEY_UNUSED;
count = bt_mesh_app_keys_get(get_idx, app_idx, ARRAY_SIZE(app_idx), 0);
if (count < 0 || count > ARRAY_SIZE(app_idx)) {
count = ARRAY_SIZE(app_idx);
}
if (prev != BT_MESH_KEY_UNUSED) {
net_buf_simple_add_le16(&msg, prev);
for (i = 0; i < count - 1; i += 2) {
key_idx_pack(&msg, app_idx[i], app_idx[i + 1]);
}
if (i < count) {
net_buf_simple_add_le16(&msg, app_idx[i]);
}
send_status:
@ -2122,9 +1955,8 @@ static void net_key_add(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
uint8_t status;
uint16_t idx;
int err;
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
@ -2134,72 +1966,17 @@ static void net_key_add(struct bt_mesh_model *model,
BT_DBG("idx 0x%04x", idx);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
int i;
status = bt_mesh_subnet_add(idx, buf->data);
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
sub = &bt_mesh.sub[i];
break;
}
}
if (!sub) {
send_net_key_status(model, ctx, idx,
STATUS_INSUFF_RESOURCES);
return;
}
}
/* Check for already existing subnet */
if (sub->net_idx == idx) {
uint8_t status;
if (memcmp(buf->data, sub->keys[0].net, 16)) {
status = STATUS_IDX_ALREADY_STORED;
} else {
status = STATUS_SUCCESS;
}
send_net_key_status(model, ctx, idx, status);
return;
}
err = bt_mesh_net_keys_create(&sub->keys[0], buf->data);
if (err) {
send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
return;
}
sub->net_idx = idx;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing NetKey persistently");
bt_mesh_store_subnet(sub);
}
/* Make sure we have valid beacon data to be sent */
bt_mesh_net_beacon_update(sub);
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
bt_mesh_proxy_beacon_send(sub);
bt_mesh_adv_update();
} else {
sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
}
send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
send_net_key_status(model, ctx, idx, status);
}
static void net_key_update(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
uint8_t status;
uint16_t idx;
int err;
idx = net_buf_simple_pull_le16(buf);
if (idx > 0xfff) {
@ -2207,59 +1984,9 @@ static void net_key_update(struct bt_mesh_model *model,
return;
}
BT_DBG("idx 0x%04x", idx);
status = bt_mesh_subnet_update(idx, buf->data);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
send_net_key_status(model, ctx, idx, STATUS_INVALID_NETKEY);
return;
}
/* The node shall successfully process a NetKey Update message on a
* valid NetKeyIndex when the NetKey value is different and the Key
* Refresh procedure has not been started, or when the NetKey value is
* the same in Phase 1. The NetKey Update message shall generate an
* error when the node is in Phase 2, or Phase 3.
*/
switch (sub->kr_phase) {
case BT_MESH_KR_NORMAL:
if (!memcmp(buf->data, sub->keys[0].net, 16)) {
return;
}
break;
case BT_MESH_KR_PHASE_1:
if (!memcmp(buf->data, sub->keys[1].net, 16)) {
send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
return;
}
__fallthrough;
case BT_MESH_KR_PHASE_2:
case BT_MESH_KR_PHASE_3:
send_net_key_status(model, ctx, idx, STATUS_CANNOT_UPDATE);
return;
}
err = bt_mesh_net_keys_create(&sub->keys[1], buf->data);
if (!err && (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
IS_ENABLED(CONFIG_BT_MESH_FRIEND))) {
err = friend_cred_update(sub);
}
if (err) {
send_net_key_status(model, ctx, idx, STATUS_UNSPECIFIED);
return;
}
sub->kr_phase = BT_MESH_KR_PHASE_1;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing NetKey persistently");
bt_mesh_store_subnet(sub);
}
bt_mesh_net_beacon_update(sub);
send_net_key_status(model, ctx, idx, STATUS_SUCCESS);
send_net_key_status(model, ctx, idx, status);
}
static void hb_pub_disable(struct bt_mesh_cfg_srv *cfg)
@ -2278,9 +2005,7 @@ static void net_key_del(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
uint16_t del_idx;
uint8_t status;
del_idx = net_buf_simple_pull_le16(buf);
if (del_idx > 0xfff) {
@ -2290,28 +2015,18 @@ static void net_key_del(struct bt_mesh_model *model,
BT_DBG("idx 0x%04x", del_idx);
sub = bt_mesh_subnet_get(del_idx);
if (!sub) {
/* This could be a retry of a previous attempt that had its
* response lost, so pretend that it was a success.
*/
status = STATUS_SUCCESS;
goto send_status;
}
/* The key that the message was encrypted with cannot be removed.
* The NetKey List must contain a minimum of one NetKey.
*/
if (ctx->net_idx == del_idx) {
status = STATUS_CANNOT_REMOVE;
goto send_status;
send_net_key_status(model, ctx, del_idx,
STATUS_CANNOT_REMOVE);
return;
}
bt_mesh_subnet_del(sub, true);
status = STATUS_SUCCESS;
bt_mesh_subnet_del(del_idx);
send_status:
send_net_key_status(model, ctx, del_idx, status);
send_net_key_status(model, ctx, del_idx, STATUS_SUCCESS);
}
static void net_key_get(struct bt_mesh_model *model,
@ -2320,29 +2035,23 @@ static void net_key_get(struct bt_mesh_model *model,
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NET_KEY_LIST,
IDX_LEN(CONFIG_BT_MESH_SUBNET_COUNT));
uint16_t prev, i;
uint16_t net_idx[CONFIG_BT_MESH_SUBNET_COUNT];
ssize_t count;
int i;
bt_mesh_model_msg_init(&msg, OP_NET_KEY_LIST);
prev = BT_MESH_KEY_UNUSED;
for (i = 0U; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (prev == BT_MESH_KEY_UNUSED) {
prev = sub->net_idx;
continue;
}
key_idx_pack(&msg, prev, sub->net_idx);
prev = BT_MESH_KEY_UNUSED;
count = bt_mesh_subnets_get(net_idx, ARRAY_SIZE(net_idx), 0);
if (count < 0 || count > ARRAY_SIZE(net_idx)) {
count = ARRAY_SIZE(net_idx);
}
if (prev != BT_MESH_KEY_UNUSED) {
net_buf_simple_add_le16(&msg, prev);
for (i = 0; i < count - 1; i += 2) {
key_idx_pack(&msg, net_idx[i], net_idx[i + 1]);
}
if (i < count) {
net_buf_simple_add_le16(&msg, net_idx[i]);
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
@ -2350,13 +2059,29 @@ static void net_key_get(struct bt_mesh_model *model,
}
}
static void send_node_id_status(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
uint8_t status,
uint16_t net_idx, uint8_t node_id)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_IDENTITY_STATUS, 4);
bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS);
net_buf_simple_add_u8(&msg, status);
net_buf_simple_add_le16(&msg, net_idx);
net_buf_simple_add_u8(&msg, node_id);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send Node Identity Status");
}
}
static void node_identity_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_IDENTITY_STATUS, 4);
struct bt_mesh_subnet *sub;
uint8_t node_id;
enum bt_mesh_feat_state node_id;
uint8_t status;
uint16_t idx;
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
@ -2369,32 +2094,16 @@ static void node_identity_get(struct bt_mesh_model *model,
return;
}
bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS);
status = bt_mesh_subnet_node_id_get(idx, &node_id);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_NETKEY);
node_id = 0x00;
} else {
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
node_id = sub->node_id;
}
net_buf_simple_add_le16(&msg, idx);
net_buf_simple_add_u8(&msg, node_id);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send Node Identity Status");
}
send_node_id_status(model, ctx, status, idx, node_id);
}
static void node_identity_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
BT_MESH_MODEL_BUF_DEFINE(msg, OP_NODE_IDENTITY_STATUS, 4);
struct bt_mesh_subnet *sub;
uint8_t node_id;
uint8_t node_id, status;
uint16_t idx;
BT_DBG("net_idx 0x%04x app_idx 0x%04x src 0x%04x len %u: %s",
@ -2413,32 +2122,21 @@ static void node_identity_set(struct bt_mesh_model *model,
return;
}
bt_mesh_model_msg_init(&msg, OP_NODE_IDENTITY_STATUS);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
net_buf_simple_add_u8(&msg, STATUS_INVALID_NETKEY);
net_buf_simple_add_le16(&msg, idx);
net_buf_simple_add_u8(&msg, node_id);
} else {
net_buf_simple_add_u8(&msg, STATUS_SUCCESS);
net_buf_simple_add_le16(&msg, idx);
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
if (node_id) {
bt_mesh_proxy_identity_start(sub);
} else {
bt_mesh_proxy_identity_stop(sub);
}
bt_mesh_adv_update();
}
net_buf_simple_add_u8(&msg, sub->node_id);
status = bt_mesh_subnet_node_id_set(idx, node_id);
if (status == STATUS_INVALID_NETKEY) {
send_node_id_status(model, ctx, status, idx,
BT_MESH_NODE_IDENTITY_STOPPED);
return;
}
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
BT_ERR("Unable to send Node Identity Status");
if (status == STATUS_FEAT_NOT_SUPP) {
/* Should return success, even if feature isn't supported: */
send_node_id_status(model, ctx, STATUS_SUCCESS, idx,
BT_MESH_NODE_IDENTITY_NOT_SUPPORTED);
return;
}
send_node_id_status(model, ctx, status, idx, node_id);
}
static void create_mod_app_status(struct net_buf_simple *msg,
@ -2727,7 +2425,7 @@ static void friend_set(struct bt_mesh_model *model,
}
if (cfg->frnd == BT_MESH_FRIEND_DISABLED) {
bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
bt_mesh_friends_clear();
}
}
@ -2802,7 +2500,7 @@ static void send_krp_status(struct bt_mesh_model *model,
static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
uint8_t kr_phase, status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
@ -2813,20 +2511,15 @@ static void krp_get(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
BT_DBG("idx 0x%04x", idx);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
} else {
send_krp_status(model, ctx, idx, sub->kr_phase,
STATUS_SUCCESS);
}
status = bt_mesh_subnet_kr_phase_get(idx, &kr_phase);
send_krp_status(model, ctx, idx, kr_phase, status);
}
static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
uint8_t phase;
uint8_t phase, status;
uint16_t idx;
idx = net_buf_simple_pull_le16(buf);
@ -2837,48 +2530,13 @@ static void krp_set(struct bt_mesh_model *model, struct bt_mesh_msg_ctx *ctx,
return;
}
BT_DBG("idx 0x%04x transition 0x%02x", idx, phase);
sub = bt_mesh_subnet_get(idx);
if (!sub) {
send_krp_status(model, ctx, idx, 0x00, STATUS_INVALID_NETKEY);
status = bt_mesh_subnet_kr_phase_set(idx, &phase);
if (status == STATUS_CANNOT_UPDATE) {
BT_ERR("Invalid kr phase transition 0x%02x", phase);
return;
}
BT_DBG("%u -> %u", sub->kr_phase, phase);
if (phase < BT_MESH_KR_PHASE_2 || phase > BT_MESH_KR_PHASE_3 ||
(sub->kr_phase == BT_MESH_KR_NORMAL &&
phase == BT_MESH_KR_PHASE_2)) {
BT_WARN("Prohibited transition %u -> %u", sub->kr_phase, phase);
return;
}
if (sub->kr_phase == BT_MESH_KR_PHASE_1 &&
phase == BT_MESH_KR_PHASE_2) {
sub->kr_phase = BT_MESH_KR_PHASE_2;
sub->kr_flag = 1;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing krp phase persistently");
bt_mesh_store_subnet(sub);
}
bt_mesh_net_beacon_update(sub);
} else if ((sub->kr_phase == BT_MESH_KR_PHASE_1 ||
sub->kr_phase == BT_MESH_KR_PHASE_2) &&
phase == BT_MESH_KR_PHASE_3) {
bt_mesh_net_revoke_keys(sub);
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
friend_cred_refresh(ctx->net_idx);
}
sub->kr_phase = BT_MESH_KR_NORMAL;
sub->kr_flag = 0;
bt_mesh_net_beacon_update(sub);
}
send_krp_status(model, ctx, idx, sub->kr_phase, STATUS_SUCCESS);
send_krp_status(model, ctx, idx, phase, status);
}
static uint8_t hb_log(uint16_t val)
@ -3382,7 +3040,6 @@ static void mod_reset(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
void bt_mesh_cfg_reset(void)
{
struct bt_mesh_cfg_srv *cfg = conf;
int i;
BT_DBG("");
@ -3392,17 +3049,6 @@ void bt_mesh_cfg_reset(void)
cfg->hb_sub.dst = BT_MESH_ADDR_UNASSIGNED;
cfg->hb_sub.expiry = 0;
/* Delete all net keys, which also takes care of all app keys which
* are associated with each net key.
*/
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx != BT_MESH_KEY_UNUSED) {
bt_mesh_subnet_del(sub, true);
}
}
bt_mesh_model_foreach(mod_reset, NULL);
(void)memset(labels, 0, sizeof(labels));
@ -3535,40 +3181,3 @@ struct bt_mesh_cfg_srv *bt_mesh_cfg_get(void)
{
return conf;
}
void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store)
{
int i;
BT_DBG("NetIdx 0x%03x store %u", sub->net_idx, store);
if (conf->hb_pub.net_idx == sub->net_idx) {
hb_pub_disable(conf);
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_store_hb_pub();
}
}
/* Delete any app keys bound to this NetKey index */
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
if (key->net_idx == sub->net_idx) {
bt_mesh_app_key_del(key, store);
}
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friend_clear_net_idx(sub->net_idx);
}
bt_mesh_net_loopback_clear(sub->net_idx);
if (IS_ENABLED(CONFIG_BT_SETTINGS) && store) {
bt_mesh_clear_subnet(sub);
}
(void)memset(sub, 0, sizeof(*sub));
sub->net_idx = BT_MESH_KEY_UNUSED;
}

View file

@ -333,67 +333,69 @@ int bt_mesh_net_decrypt(const uint8_t key[16], struct net_buf_simple *buf,
&buf->data[7], mic_len);
}
static void create_app_nonce(uint8_t nonce[13], bool dev_key, uint8_t aszmic,
uint16_t src, uint16_t dst, uint32_t seq_num,
uint32_t iv_index)
static void create_app_nonce(uint8_t nonce[13],
const struct bt_mesh_app_crypto_ctx *ctx)
{
if (dev_key) {
if (ctx->dev_key) {
nonce[0] = 0x02;
} else {
nonce[0] = 0x01;
}
sys_put_be32((seq_num | ((uint32_t)aszmic << 31)), &nonce[1]);
sys_put_be32((ctx->seq_num | ((uint32_t)ctx->aszmic << 31)), &nonce[1]);
sys_put_be16(src, &nonce[5]);
sys_put_be16(dst, &nonce[7]);
sys_put_be16(ctx->src, &nonce[5]);
sys_put_be16(ctx->dst, &nonce[7]);
sys_put_be32(iv_index, &nonce[9]);
sys_put_be32(ctx->iv_index, &nonce[9]);
}
int bt_mesh_app_encrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
struct net_buf_simple *buf, const uint8_t *ad,
uint16_t src, uint16_t dst, uint32_t seq_num, uint32_t iv_index)
int bt_mesh_app_encrypt(const uint8_t key[16],
const struct bt_mesh_app_crypto_ctx *ctx,
struct net_buf_simple *buf)
{
uint8_t nonce[13];
int err;
BT_DBG("AppKey %s", bt_hex(key, 16));
BT_DBG("dev_key %u src 0x%04x dst 0x%04x", dev_key, src, dst);
BT_DBG("seq_num 0x%08x iv_index 0x%08x", seq_num, iv_index);
BT_DBG("dev_key %u src 0x%04x dst 0x%04x", ctx->dev_key, ctx->src,
ctx->dst);
BT_DBG("seq_num 0x%08x iv_index 0x%08x", ctx->seq_num, ctx->iv_index);
BT_DBG("Clear: %s", bt_hex(buf->data, buf->len));
create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
create_app_nonce(nonce, ctx);
BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_ccm_encrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
buf->data, APP_MIC_LEN(aszmic));
err = bt_ccm_encrypt(key, nonce, buf->data, buf->len, ctx->ad,
ctx->ad ? 16 : 0, buf->data,
APP_MIC_LEN(ctx->aszmic));
if (!err) {
net_buf_simple_add(buf, APP_MIC_LEN(aszmic));
net_buf_simple_add(buf, APP_MIC_LEN(ctx->aszmic));
BT_DBG("Encr: %s", bt_hex(buf->data, buf->len));
}
return err;
}
int bt_mesh_app_decrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
struct net_buf_simple *buf, struct net_buf_simple *out,
const uint8_t *ad, uint16_t src, uint16_t dst, uint32_t seq_num,
uint32_t iv_index)
int bt_mesh_app_decrypt(const uint8_t key[16],
const struct bt_mesh_app_crypto_ctx *ctx,
struct net_buf_simple *buf, struct net_buf_simple *out)
{
uint8_t nonce[13];
int err;
BT_DBG("EncData (len %u) %s", buf->len, bt_hex(buf->data, buf->len));
BT_DBG("EncData (len %u) %s", buf->len,
bt_hex(buf->data, buf->len));
create_app_nonce(nonce, dev_key, aszmic, src, dst, seq_num, iv_index);
create_app_nonce(nonce, ctx);
BT_DBG("AppKey %s", bt_hex(key, 16));
BT_DBG("Nonce %s", bt_hex(nonce, 13));
err = bt_ccm_decrypt(key, nonce, buf->data, buf->len, ad, ad ? 16 : 0,
out->data, APP_MIC_LEN(aszmic));
err = bt_ccm_decrypt(key, nonce, buf->data, buf->len, ctx->ad,
ctx->ad ? 16 : 0, out->data,
APP_MIC_LEN(ctx->aszmic));
if (!err) {
net_buf_simple_add(out, buf->len);
}

View file

@ -127,14 +127,24 @@ int bt_mesh_net_encrypt(const uint8_t key[16], struct net_buf_simple *buf,
int bt_mesh_net_decrypt(const uint8_t key[16], struct net_buf_simple *buf,
uint32_t iv_index, bool proxy);
int bt_mesh_app_encrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
struct net_buf_simple *buf, const uint8_t *ad,
uint16_t src, uint16_t dst, uint32_t seq_num, uint32_t iv_index);
int bt_mesh_app_decrypt(const uint8_t key[16], bool dev_key, uint8_t aszmic,
struct net_buf_simple *buf, struct net_buf_simple *out,
const uint8_t *ad, uint16_t src, uint16_t dst, uint32_t seq_num,
uint32_t iv_index);
struct bt_mesh_app_crypto_ctx {
bool dev_key;
uint8_t aszmic;
uint16_t src;
uint16_t dst;
uint32_t seq_num;
uint32_t iv_index;
const uint8_t *ad;
};
int bt_mesh_app_encrypt(const uint8_t key[16],
const struct bt_mesh_app_crypto_ctx *ctx,
struct net_buf_simple *buf);
int bt_mesh_app_decrypt(const uint8_t key[16],
const struct bt_mesh_app_crypto_ctx *ctx,
struct net_buf_simple *buf, struct net_buf_simple *out);
uint8_t bt_mesh_fcs_calc(const uint8_t *data, uint8_t data_len);

View file

@ -145,11 +145,6 @@ uint8_t bt_mesh_beacon_get(void);
uint8_t bt_mesh_gatt_proxy_get(void);
uint8_t bt_mesh_default_ttl_get(void);
void bt_mesh_subnet_del(struct bt_mesh_subnet *sub, bool store);
struct bt_mesh_app_key *bt_mesh_app_key_alloc(uint16_t app_idx);
void bt_mesh_app_key_del(struct bt_mesh_app_key *key, bool store);
#include <sys/byteorder.h>
static inline void key_idx_pack(struct net_buf_simple *buf,

View file

@ -22,6 +22,7 @@
#include "adv.h"
#include "mesh.h"
#include "net.h"
#include "app_keys.h"
#include "transport.h"
#include "access.h"
#include "foundation.h"
@ -85,7 +86,7 @@ struct bt_mesh_friend *bt_mesh_friend_find(uint16_t net_idx, uint16_t lpn_addr,
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (valid && !frnd->valid) {
if (valid && !frnd->subnet) {
continue;
}
@ -93,7 +94,8 @@ struct bt_mesh_friend *bt_mesh_friend_find(uint16_t net_idx, uint16_t lpn_addr,
continue;
}
if (net_idx != BT_MESH_KEY_ANY && frnd->net_idx != net_idx) {
if (net_idx != BT_MESH_KEY_ANY &&
(!frnd->subnet || frnd->subnet->net_idx != net_idx)) {
continue;
}
@ -105,6 +107,14 @@ struct bt_mesh_friend *bt_mesh_friend_find(uint16_t net_idx, uint16_t lpn_addr,
return NULL;
}
static int friend_cred_create(struct bt_mesh_friend *frnd, uint8_t idx)
{
return bt_mesh_friend_cred_create(&frnd->cred[idx], frnd->lpn,
bt_mesh_primary_addr(),
frnd->lpn_counter, frnd->counter,
frnd->subnet->keys[idx].net);
}
static void purge_buffers(sys_slist_t *list)
{
while (!sys_slist_is_empty(list)) {
@ -141,7 +151,7 @@ static void friend_clear(struct bt_mesh_friend *frnd)
k_delayed_work_cancel(&frnd->timer);
friend_cred_del(frnd->net_idx, frnd->lpn);
memset(frnd->cred, 0, sizeof(frnd->cred));
if (frnd->last) {
/* Cancel the sending if necessary */
@ -162,7 +172,7 @@ static void friend_clear(struct bt_mesh_friend *frnd)
seg->seg_count = 0U;
}
frnd->valid = 0U;
frnd->subnet = NULL;
frnd->established = 0U;
frnd->pending_buf = 0U;
frnd->fsn = 0U;
@ -171,22 +181,20 @@ static void friend_clear(struct bt_mesh_friend *frnd)
(void)memset(frnd->sub_list, 0, sizeof(frnd->sub_list));
}
void bt_mesh_friend_clear_net_idx(uint16_t net_idx)
void bt_mesh_friends_clear(void)
{
int i;
BT_DBG("net_idx 0x%04x", net_idx);
BT_DBG("");
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
if (!frnd->subnet) {
continue;
}
if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
friend_clear(frnd);
}
friend_clear(frnd);
}
}
@ -201,11 +209,12 @@ void bt_mesh_friend_sec_update(uint16_t net_idx)
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (frnd->net_idx == BT_MESH_KEY_UNUSED) {
if (!frnd->subnet) {
continue;
}
if (net_idx == BT_MESH_KEY_ANY || frnd->net_idx == net_idx) {
if (net_idx == BT_MESH_KEY_ANY ||
frnd->subnet->net_idx == net_idx) {
enqueue_update(frnd, 0x00);
}
}
@ -323,12 +332,10 @@ static struct net_buf *create_friend_pdu(struct bt_mesh_friend *frnd,
}
struct unseg_app_sdu_meta {
struct bt_mesh_net_rx net;
struct bt_mesh_app_crypto_ctx crypto;
const uint8_t *key;
struct bt_mesh_subnet *subnet;
bool is_dev_key;
uint8_t aid;
uint8_t *ad;
};
static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
@ -336,24 +343,30 @@ static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
struct unseg_app_sdu_meta *meta)
{
uint16_t app_idx = FRIEND_ADV(buf)->app_idx;
struct bt_mesh_net_rx net;
int err;
meta->subnet = bt_mesh_subnet_get(frnd->net_idx);
meta->is_dev_key = BT_MESH_IS_DEV_KEY(app_idx);
bt_mesh_net_header_parse(&buf->b, &meta->net);
err = bt_mesh_app_key_get(meta->subnet, app_idx, meta->net.ctx.recv_dst,
&meta->key, &meta->aid);
meta->subnet = frnd->subnet;
bt_mesh_net_header_parse(&buf->b, &net);
err = bt_mesh_keys_resolve(&net.ctx, &net.sub, &meta->key, &meta->aid);
if (err) {
return err;
}
if (BT_MESH_ADDR_IS_VIRTUAL(meta->net.ctx.recv_dst)) {
meta->ad = bt_mesh_label_uuid_get(meta->net.ctx.recv_dst);
if (!meta->ad) {
meta->crypto.src = net.ctx.addr;
meta->crypto.dst = net.ctx.recv_dst;
meta->crypto.iv_index = BT_MESH_NET_IVI_TX;
meta->crypto.dev_key = BT_MESH_IS_DEV_KEY(app_idx);
meta->crypto.seq_num = net.seq;
meta->crypto.aszmic = 0;
if (BT_MESH_ADDR_IS_VIRTUAL(meta->crypto.dst)) {
meta->crypto.ad = bt_mesh_label_uuid_get(meta->crypto.dst);
if (!meta->crypto.ad) {
return -ENOENT;
}
} else {
meta->ad = NULL;
meta->crypto.ad = NULL;
}
return 0;
@ -369,10 +382,7 @@ static int unseg_app_sdu_decrypt(struct bt_mesh_friend *frnd,
net_buf_simple_pull(&sdu, 10);
sdu.len -= 4;
return bt_mesh_app_decrypt(meta->key, meta->is_dev_key, 0, &sdu, &sdu,
meta->ad, meta->net.ctx.addr,
meta->net.ctx.recv_dst, meta->net.seq,
BT_MESH_NET_IVI_TX);
return bt_mesh_app_decrypt(meta->key, &meta->crypto, &sdu, &sdu);
}
static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd,
@ -385,10 +395,7 @@ static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd,
net_buf_simple_pull(&sdu, 10);
sdu.len -= 4;
return bt_mesh_app_encrypt(meta->key, meta->is_dev_key, 0, &sdu,
meta->ad, meta->net.ctx.addr,
meta->net.ctx.recv_dst, bt_mesh.seq,
BT_MESH_NET_IVI_TX);
return bt_mesh_app_encrypt(meta->key, &meta->crypto, &sdu);
}
static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
@ -409,7 +416,7 @@ static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
/* No need to reencrypt the message if the sequence number is
* unchanged.
*/
if (meta.net.seq == bt_mesh.seq) {
if (meta.crypto.seq_num == bt_mesh.seq) {
return 0;
}
@ -430,22 +437,16 @@ static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct net_buf *buf,
bool master_cred)
{
struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
const uint8_t *enc, *priv;
const struct bt_mesh_net_cred *cred;
uint32_t iv_index;
uint16_t src;
uint8_t nid;
int err;
if (master_cred) {
enc = sub->keys[sub->kr_flag].enc;
priv = sub->keys[sub->kr_flag].privacy;
nid = sub->keys[sub->kr_flag].nid;
cred = &frnd->subnet->keys[SUBNET_KEY_TX_IDX(frnd->subnet)]
.msg;
} else {
if (friend_cred_get(sub, frnd->lpn, &nid, &enc, &priv)) {
BT_ERR("friend_cred_get failed");
return -ENOENT;
}
cred = &frnd->cred[SUBNET_KEY_TX_IDX(frnd->subnet)];
}
src = sys_get_be16(&buf->data[5]);
@ -470,14 +471,14 @@ static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct net_buf *buf,
iv_index = (bt_mesh.iv_index - ((bt_mesh.iv_index & 1) != ivi));
}
buf->data[0] = (nid | (iv_index & 1) << 7);
buf->data[0] = (cred->nid | (iv_index & 1) << 7);
if (bt_mesh_net_encrypt(enc, &buf->b, iv_index, false)) {
if (bt_mesh_net_encrypt(cred->enc, &buf->b, iv_index, false)) {
BT_ERR("Encrypting failed");
return -EINVAL;
}
if (bt_mesh_net_obfuscate(buf->data, iv_index, priv)) {
if (bt_mesh_net_obfuscate(buf->data, iv_index, cred->privacy)) {
BT_ERR("Obfuscating failed");
return -EINVAL;
}
@ -512,16 +513,15 @@ static struct net_buf *encode_update(struct bt_mesh_friend *frnd, uint8_t md)
{
struct bt_mesh_ctl_friend_update *upd;
NET_BUF_SIMPLE_DEFINE(sdu, 1 + sizeof(*upd));
struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
__ASSERT_NO_MSG(sub != NULL);
__ASSERT_NO_MSG(frnd->subnet);
BT_DBG("lpn 0x%04x md 0x%02x", frnd->lpn, md);
net_buf_simple_reserve(&sdu, 1);
upd = net_buf_simple_add(&sdu, sizeof(*upd));
upd->flags = bt_mesh_net_flags(sub);
upd->flags = bt_mesh_net_flags(frnd->subnet);
upd->iv_index = sys_cpu_to_be32(bt_mesh.iv_index);
upd->md = md;
@ -742,13 +742,13 @@ static const struct bt_mesh_send_cb clear_sent_cb = {
static void send_friend_clear(struct bt_mesh_friend *frnd)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = frnd->net_idx,
.net_idx = frnd->subnet->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = frnd->clear.frnd,
.send_ttl = BT_MESH_TTL_MAX,
};
struct bt_mesh_net_tx tx = {
.sub = bt_mesh_subnet_get(frnd->net_idx),
.sub = frnd->subnet,
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = bt_mesh_net_transmit_get(),
@ -903,7 +903,7 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
struct bt_mesh_ctl_friend_req *msg = (void *)buf->data;
struct bt_mesh_friend *frnd = NULL;
uint32_t poll_to;
int i;
int i, err;
if (rx->net_if == BT_MESH_NET_IF_LOCAL) {
BT_DBG("Ignoring Friend request from local interface");
@ -957,9 +957,8 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
if (!bt_mesh.frnd[i].valid) {
if (!bt_mesh.frnd[i].subnet) {
frnd = &bt_mesh.frnd[i];
frnd->valid = 1U;
break;
}
}
@ -972,12 +971,19 @@ int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
init_friend:
frnd->lpn = rx->ctx.addr;
frnd->num_elem = msg->num_elem;
frnd->net_idx = rx->sub->net_idx;
frnd->subnet = rx->sub;
frnd->recv_delay = msg->recv_delay;
frnd->poll_to = poll_to * 100U;
frnd->lpn_counter = sys_be16_to_cpu(msg->lpn_counter);
frnd->clear.frnd = sys_be16_to_cpu(msg->prev_addr);
err = friend_cred_create(frnd, SUBNET_KEY_TX_IDX(frnd->subnet));
if (err) {
BT_ERR("Failed to create friend credentials");
friend_clear(frnd);
return -EIO;
}
BT_DBG("LPN 0x%04x rssi %d recv_delay %u poll_to %ums",
frnd->lpn, rx->ctx.recv_rssi, frnd->recv_delay, frnd->poll_to);
@ -990,9 +996,6 @@ init_friend:
K_MSEC(offer_delay(frnd, rx->ctx.recv_rssi,
msg->criteria)));
friend_cred_create(rx->sub, frnd->lpn, frnd->lpn_counter,
frnd->counter);
enqueue_offer(frnd, rx->ctx.recv_rssi);
return 0;
@ -1207,6 +1210,53 @@ send_last:
bt_mesh_adv_send(frnd->last, &buf_sent_cb, frnd);
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
int i, err;
if (evt == BT_MESH_KEY_ADDED) {
return;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (frnd->subnet != sub) {
continue;
}
switch (evt) {
case BT_MESH_KEY_DELETED:
BT_DBG("Cleared network for 0x%04x", frnd->lpn);
friend_clear(frnd);
break;
case BT_MESH_KEY_UPDATED:
BT_DBG("Generating new keys for 0x%04x", frnd->lpn);
err = friend_cred_create(frnd, 1);
if (err) {
BT_ERR("Failed updating friend cred for 0x%04x",
frnd->lpn);
friend_clear(frnd);
break;
}
enqueue_update(frnd, 0);
break;
case BT_MESH_KEY_REVOKED:
BT_DBG("Revoking old keys for 0x%04x", frnd->lpn);
memcpy(&frnd->cred[0], &frnd->cred[1],
sizeof(frnd->cred[0]));
memset(&frnd->cred[1], 0, sizeof(frnd->cred[1]));
enqueue_update(frnd, 0);
break;
default:
break;
}
}
}
BT_MESH_SUBNET_CB_DEFINE(subnet_evt);
int bt_mesh_friend_init(void)
{
int i;
@ -1215,8 +1265,6 @@ int bt_mesh_friend_init(void)
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
int j;
frnd->net_idx = BT_MESH_KEY_UNUSED;
sys_slist_init(&frnd->queue);
k_delayed_work_init(&frnd->timer, friend_timeout);
@ -1394,7 +1442,7 @@ static bool friend_lpn_matches(struct bt_mesh_friend *frnd, uint16_t net_idx,
return false;
}
if (net_idx != frnd->net_idx) {
if (net_idx != frnd->subnet->net_idx) {
return false;
}

View file

@ -34,7 +34,7 @@ void bt_mesh_friend_clear_incomplete(struct bt_mesh_subnet *sub, uint16_t src,
void bt_mesh_friend_sec_update(uint16_t net_idx);
void bt_mesh_friend_clear_net_idx(uint16_t net_idx);
void bt_mesh_friends_clear(void);
int bt_mesh_friend_poll(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
int bt_mesh_friend_req(struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);

View file

@ -175,20 +175,20 @@ static const struct bt_mesh_send_cb clear_sent_cb = {
static int send_friend_clear(void)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.net_idx = bt_mesh.lpn.sub->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = bt_mesh.lpn.frnd,
.send_ttl = 0,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.sub = bt_mesh.lpn.sub,
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = bt_mesh_net_transmit_get(),
};
struct bt_mesh_ctl_friend_clear req = {
.lpn_addr = sys_cpu_to_be16(tx.src),
.lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.counter),
.lpn_counter = sys_cpu_to_be16(bt_mesh.lpn.lpn_counter),
};
BT_DBG("");
@ -215,8 +215,6 @@ static void clear_friendship(bool force, bool disable)
k_delayed_work_cancel(&lpn->timer);
friend_cred_del(bt_mesh.sub[0].net_idx, lpn->frnd);
if (lpn->clear_success) {
lpn->old_friend = BT_MESH_ADDR_UNASSIGNED;
} else {
@ -236,6 +234,7 @@ static void clear_friendship(bool force, bool disable)
lpn->sent_req = 0U;
lpn->established = 0U;
lpn->clear_success = 0U;
lpn->sub = NULL;
group_zero(lpn->added);
group_zero(lpn->pending);
@ -289,13 +288,13 @@ static int send_friend_req(struct bt_mesh_lpn *lpn)
{
const struct bt_mesh_comp *comp = bt_mesh_comp_get();
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.net_idx = lpn->sub->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = BT_MESH_ADDR_FRIENDS,
.send_ttl = 0,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.sub = bt_mesh.lpn.sub,
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = POLL_XMIT,
@ -306,7 +305,7 @@ static int send_friend_req(struct bt_mesh_lpn *lpn)
.poll_to = LPN_POLL_TO,
.prev_addr = sys_cpu_to_be16(lpn->old_friend),
.num_elem = comp->elem_count,
.lpn_counter = sys_cpu_to_be16(lpn->counter),
.lpn_counter = sys_cpu_to_be16(lpn->lpn_counter),
};
BT_DBG("");
@ -355,13 +354,13 @@ static const struct bt_mesh_send_cb req_sent_cb = {
static int send_friend_poll(void)
{
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.net_idx = bt_mesh.lpn.sub->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = bt_mesh.lpn.frnd,
.send_ttl = 0,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.sub = bt_mesh.lpn.sub,
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = POLL_XMIT,
@ -482,13 +481,21 @@ void bt_mesh_lpn_msg_received(struct bt_mesh_net_rx *rx)
send_friend_poll();
}
static int friend_cred_create(struct bt_mesh_net_cred *cred,
const uint8_t key[16])
{
struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
return bt_mesh_friend_cred_create(cred, bt_mesh_primary_addr(),
lpn->frnd, lpn->lpn_counter,
lpn->frnd_counter, key);
}
int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
struct bt_mesh_ctl_friend_offer *msg = (void *)buf->data;
struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
struct bt_mesh_subnet *sub = rx->sub;
struct friend_cred *cred;
uint16_t frnd_counter;
int err;
@ -513,16 +520,26 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
msg->recv_win, msg->queue_size, msg->sub_list_size, msg->rssi,
frnd_counter);
lpn->frnd_counter = frnd_counter;
lpn->frnd = rx->ctx.addr;
lpn->sub = rx->sub;
cred = friend_cred_create(sub, lpn->frnd, lpn->counter, frnd_counter);
if (!cred) {
lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
return -ENOMEM;
/* Create friend credentials for each of the valid keys in the
* friendship subnet:
*/
for (int i = 0; i < ARRAY_SIZE(lpn->cred); i++) {
if (!lpn->sub->keys[i].valid) {
continue;
}
err = friend_cred_create(&lpn->cred[i], lpn->sub->keys[i].net);
if (err) {
lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
return err;
}
}
/* TODO: Add offer acceptance criteria check */
k_delayed_work_cancel(&lpn->timer);
lpn->recv_win = msg->recv_win;
@ -530,14 +547,14 @@ int bt_mesh_lpn_friend_offer(struct bt_mesh_net_rx *rx,
err = send_friend_poll();
if (err) {
friend_cred_clear(cred);
lpn->sub = NULL;
lpn->frnd = BT_MESH_ADDR_UNASSIGNED;
lpn->recv_win = 0U;
lpn->queue_size = 0U;
return err;
}
lpn->counter++;
lpn->lpn_counter++;
return 0;
}
@ -564,7 +581,7 @@ int bt_mesh_lpn_friend_clear_cfm(struct bt_mesh_net_rx *rx,
BT_DBG("LPNAddress 0x%04x LPNCounter 0x%04x", addr, counter);
if (addr != bt_mesh_primary_addr() || counter != lpn->counter) {
if (addr != bt_mesh_primary_addr() || counter != lpn->lpn_counter) {
BT_WARN("Invalid parameters in Friend Clear Confirm");
return 0;
}
@ -637,13 +654,13 @@ static bool sub_update(uint8_t op)
struct bt_mesh_lpn *lpn = &bt_mesh.lpn;
int added_count = group_popcount(lpn->added);
struct bt_mesh_msg_ctx ctx = {
.net_idx = bt_mesh.sub[0].net_idx,
.net_idx = lpn->sub->net_idx,
.app_idx = BT_MESH_KEY_UNUSED,
.addr = lpn->frnd,
.send_ttl = 0,
};
struct bt_mesh_net_tx tx = {
.sub = &bt_mesh.sub[0],
.sub = lpn->sub,
.ctx = &ctx,
.src = bt_mesh_primary_addr(),
.xmit = POLL_XMIT,
@ -764,7 +781,7 @@ static void lpn_timeout(struct k_work *work)
if (IS_ENABLED(CONFIG_BT_MESH_LPN_ESTABLISHMENT)) {
bt_mesh_scan_disable();
}
lpn->counter++;
lpn->lpn_counter++;
lpn_set_state(BT_MESH_LPN_ENABLED);
lpn->sent_req = 0U;
k_delayed_work_submit(&lpn->timer, FRIEND_REQ_RETRY_TIMEOUT);
@ -985,11 +1002,7 @@ int bt_mesh_lpn_friend_update(struct bt_mesh_net_rx *rx,
BT_DBG("flags 0x%02x iv_index 0x%08x md %u", msg->flags, iv_index,
msg->md);
if (bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags),
rx->new_key)) {
bt_mesh_net_beacon_update(sub);
}
bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(msg->flags), rx->new_key);
bt_mesh_net_iv_update(iv_index, BT_MESH_IV_UPDATE(msg->flags));
if (lpn->groups_changed) {
@ -1029,6 +1042,26 @@ void bt_mesh_lpn_set_cb(void (*cb)(uint16_t friend_addr, bool established))
lpn_cb = cb;
}
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
switch (evt) {
case BT_MESH_KEY_DELETED:
if (sub == bt_mesh.lpn.sub) {
BT_DBG("NetKey deleted");
clear_friendship(true, false);
}
break;
case BT_MESH_KEY_UPDATED:
BT_DBG("NetKey updated");
friend_cred_create(&bt_mesh.lpn.cred[1], sub->keys[1].net);
break;
default:
break;
}
}
BT_MESH_SUBNET_CB_DEFINE(subnet_evt);
int bt_mesh_lpn_init(void)
{
struct bt_mesh_lpn *lpn = &bt_mesh.lpn;

View file

@ -24,6 +24,7 @@
#include "adv.h"
#include "prov.h"
#include "net.h"
#include "app_keys.h"
#include "rpl.h"
#include "beacon.h"
#include "lpn.h"
@ -129,13 +130,6 @@ int bt_mesh_provision(const uint8_t net_key[16], uint16_t net_idx,
bt_mesh_lpn_group_add(BT_MESH_ADDR_ALL_NODES);
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing network information persistently");
bt_mesh_store_net();
bt_mesh_store_subnet(&bt_mesh.sub[0]);
bt_mesh_store_iv(false);
}
bt_mesh_start();
return 0;
@ -178,6 +172,8 @@ void bt_mesh_reset(void)
bt_mesh_rx_reset();
bt_mesh_tx_reset();
bt_mesh_app_keys_reset();
bt_mesh_net_keys_reset();
bt_mesh_net_loopback_clear(BT_MESH_KEY_ANY);
@ -192,7 +188,7 @@ void bt_mesh_reset(void)
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friend_clear_net_idx(BT_MESH_KEY_ANY);
bt_mesh_friends_clear();
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {

View file

@ -7,13 +7,39 @@
*/
#define BT_MESH_KEY_PRIMARY 0x0000
#define BT_MESH_KEY_ANY 0xffff
#define BT_MESH_ADDR_IS_UNICAST(addr) ((addr) && (addr) < 0x8000)
#define BT_MESH_ADDR_IS_GROUP(addr) ((addr) >= 0xc000 && (addr) <= 0xff00)
#define BT_MESH_ADDR_IS_VIRTUAL(addr) ((addr) >= 0x8000 && (addr) < 0xc000)
#define BT_MESH_ADDR_IS_RFU(addr) ((addr) >= 0xff00 && (addr) <= 0xfffb)
enum bt_mesh_key_evt {
BT_MESH_KEY_ADDED, /* New key added */
BT_MESH_KEY_DELETED, /* Existing key deleted */
BT_MESH_KEY_UPDATED, /* KR phase 1, second key added */
BT_MESH_KEY_SWAPPED, /* KR phase 2, now sending on second key */
BT_MESH_KEY_REVOKED, /* KR phase 3, old key removed */
};
/** Appkey callback. Instantiate with @ref BT_MESH_APP_KEY_CB */
struct bt_mesh_app_key_cb {
void (*evt_handler)(uint16_t app_idx, uint16_t net_idx,
enum bt_mesh_key_evt evt);
};
/** @def BT_MESH_APP_KEY_CB
*
* @brief Register an AppKey event callback.
*
* @param _handler Handler function, see @ref bt_mesh_app_key_cb::evt_handler.
*/
#define BT_MESH_APP_KEY_CB_DEFINE(_handler) \
static const Z_STRUCT_SECTION_ITERABLE(bt_mesh_app_key_cb, \
_CONCAT(bt_mesh_app_key_cb_, \
_handler)) = { \
.evt_handler = (_handler), \
}
struct bt_mesh_net;
int bt_mesh_start(void);

View file

@ -61,17 +61,6 @@
#define SRC(pdu) (sys_get_be16(&(pdu)[5]))
#define DST(pdu) (sys_get_be16(&(pdu)[7]))
/* Determine how many friendship credentials we need */
#if defined(CONFIG_BT_MESH_FRIEND)
#define FRIEND_CRED_COUNT CONFIG_BT_MESH_FRIEND_LPN_COUNT
#elif defined(CONFIG_BT_MESH_LOW_POWER)
#define FRIEND_CRED_COUNT CONFIG_BT_MESH_SUBNET_COUNT
#else
#define FRIEND_CRED_COUNT 0
#endif
static struct friend_cred friend_cred[FRIEND_CRED_COUNT];
static struct {
uint32_t src : 15, /* MSb of source is always 0 */
seq : 17;
@ -81,16 +70,6 @@ static uint16_t msg_cache_next;
/* Singleton network context (the implementation only supports one) */
struct bt_mesh_net bt_mesh = {
.local_queue = SYS_SLIST_STATIC_INIT(&bt_mesh.local_queue),
.sub = {
[0 ... (CONFIG_BT_MESH_SUBNET_COUNT - 1)] = {
.net_idx = BT_MESH_KEY_UNUSED,
}
},
.app_keys = {
[0 ... (CONFIG_BT_MESH_APP_KEY_COUNT - 1)] = {
.net_idx = BT_MESH_KEY_UNUSED,
}
},
};
NET_BUF_POOL_DEFINE(loopback_buf_pool, CONFIG_BT_MESH_LOOPBACK_BUFS,
@ -141,330 +120,29 @@ static void msg_cache_add(struct bt_mesh_net_rx *rx)
msg_cache_next %= ARRAY_SIZE(msg_cache);
}
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx)
{
int i;
if (net_idx == BT_MESH_KEY_ANY) {
return &bt_mesh.sub[0];
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
if (bt_mesh.sub[i].net_idx == net_idx) {
return &bt_mesh.sub[i];
}
}
return NULL;
}
int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
const uint8_t key[16])
{
uint8_t p[] = { 0 };
uint8_t nid;
int err;
err = bt_mesh_k2(key, p, sizeof(p), &nid, keys->enc, keys->privacy);
if (err) {
BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
return err;
}
memcpy(keys->net, key, 16);
keys->nid = nid;
BT_DBG("NID 0x%02x EncKey %s", keys->nid, bt_hex(keys->enc, 16));
BT_DBG("PrivacyKey %s", bt_hex(keys->privacy, 16));
err = bt_mesh_k3(key, keys->net_id);
if (err) {
BT_ERR("Unable to generate Net ID");
return err;
}
BT_DBG("NetID %s", bt_hex(keys->net_id, 8));
#if defined(CONFIG_BT_MESH_GATT_PROXY)
err = bt_mesh_identity_key(key, keys->identity);
if (err) {
BT_ERR("Unable to generate IdentityKey");
return err;
}
BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16));
#endif /* GATT_PROXY */
err = bt_mesh_beacon_key(key, keys->beacon);
if (err) {
BT_ERR("Unable to generate beacon key");
return err;
}
BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
return 0;
}
int friend_cred_set(struct friend_cred *cred, uint8_t idx, const uint8_t net_key[16])
{
uint16_t lpn_addr, frnd_addr;
int err;
uint8_t p[9];
#if defined(CONFIG_BT_MESH_LOW_POWER)
if (cred->addr == bt_mesh.lpn.frnd) {
lpn_addr = bt_mesh_primary_addr();
frnd_addr = cred->addr;
} else {
lpn_addr = cred->addr;
frnd_addr = bt_mesh_primary_addr();
}
#else
lpn_addr = cred->addr;
frnd_addr = bt_mesh_primary_addr();
#endif
BT_DBG("LPNAddress 0x%04x FriendAddress 0x%04x", lpn_addr, frnd_addr);
BT_DBG("LPNCounter 0x%04x FriendCounter 0x%04x", cred->lpn_counter,
cred->frnd_counter);
p[0] = 0x01;
sys_put_be16(lpn_addr, p + 1);
sys_put_be16(frnd_addr, p + 3);
sys_put_be16(cred->lpn_counter, p + 5);
sys_put_be16(cred->frnd_counter, p + 7);
err = bt_mesh_k2(net_key, p, sizeof(p), &cred->cred[idx].nid,
cred->cred[idx].enc, cred->cred[idx].privacy);
if (err) {
BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
return err;
}
BT_DBG("Friend NID 0x%02x EncKey %s", cred->cred[idx].nid,
bt_hex(cred->cred[idx].enc, 16));
BT_DBG("Friend PrivacyKey %s", bt_hex(cred->cred[idx].privacy, 16));
return 0;
}
void friend_cred_refresh(uint16_t net_idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
struct friend_cred *cred = &friend_cred[i];
if (cred->addr != BT_MESH_ADDR_UNASSIGNED &&
cred->net_idx == net_idx) {
memcpy(&cred->cred[0], &cred->cred[1],
sizeof(cred->cred[0]));
}
}
}
int friend_cred_update(struct bt_mesh_subnet *sub)
{
int err, i;
BT_DBG("net_idx 0x%04x", sub->net_idx);
for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
struct friend_cred *cred = &friend_cred[i];
if (cred->addr == BT_MESH_ADDR_UNASSIGNED ||
cred->net_idx != sub->net_idx) {
continue;
}
err = friend_cred_set(cred, 1, sub->keys[1].net);
if (err) {
return err;
}
}
return 0;
}
struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, uint16_t addr,
uint16_t lpn_counter, uint16_t frnd_counter)
{
struct friend_cred *cred;
int i, err;
BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
for (cred = NULL, i = 0; i < ARRAY_SIZE(friend_cred); i++) {
if ((friend_cred[i].addr == BT_MESH_ADDR_UNASSIGNED) ||
(friend_cred[i].addr == addr &&
friend_cred[i].net_idx == sub->net_idx)) {
cred = &friend_cred[i];
break;
}
}
if (!cred) {
BT_WARN("No free friend credential slots");
return NULL;
}
cred->net_idx = sub->net_idx;
cred->addr = addr;
cred->lpn_counter = lpn_counter;
cred->frnd_counter = frnd_counter;
err = friend_cred_set(cred, 0, sub->keys[0].net);
if (err) {
friend_cred_clear(cred);
return NULL;
}
if (sub->kr_flag) {
err = friend_cred_set(cred, 1, sub->keys[1].net);
if (err) {
friend_cred_clear(cred);
return NULL;
}
}
return cred;
}
void friend_cred_clear(struct friend_cred *cred)
{
cred->net_idx = BT_MESH_KEY_UNUSED;
cred->addr = BT_MESH_ADDR_UNASSIGNED;
cred->lpn_counter = 0U;
cred->frnd_counter = 0U;
(void)memset(cred->cred, 0, sizeof(cred->cred));
}
int friend_cred_del(uint16_t net_idx, uint16_t addr)
{
int i;
for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
struct friend_cred *cred = &friend_cred[i];
if (cred->addr == addr && cred->net_idx == net_idx) {
friend_cred_clear(cred);
return 0;
}
}
return -ENOENT;
}
int friend_cred_get(struct bt_mesh_subnet *sub, uint16_t addr, uint8_t *nid,
const uint8_t **enc, const uint8_t **priv)
{
int i;
BT_DBG("net_idx 0x%04x addr 0x%04x", sub->net_idx, addr);
for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
struct friend_cred *cred = &friend_cred[i];
if (cred->net_idx != sub->net_idx) {
continue;
}
if (addr != BT_MESH_ADDR_UNASSIGNED && cred->addr != addr) {
continue;
}
if (nid) {
*nid = cred->cred[sub->kr_flag].nid;
}
if (enc) {
*enc = cred->cred[sub->kr_flag].enc;
}
if (priv) {
*priv = cred->cred[sub->kr_flag].privacy;
}
return 0;
}
return -ENOENT;
}
uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
{
uint8_t flags = 0x00;
if (sub && sub->kr_flag) {
flags |= BT_MESH_NET_FLAG_KR;
}
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
flags |= BT_MESH_NET_FLAG_IVU;
}
return flags;
}
int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub)
{
uint8_t flags = bt_mesh_net_flags(sub);
struct bt_mesh_subnet_keys *keys;
if (sub->kr_flag) {
BT_DBG("NetIndex %u Using new key", sub->net_idx);
keys = &sub->keys[1];
} else {
BT_DBG("NetIndex %u Using current key", sub->net_idx);
keys = &sub->keys[0];
}
BT_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index);
return bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id,
bt_mesh.iv_index, sub->auth);
}
int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
uint32_t iv_index)
{
struct bt_mesh_subnet *sub;
int err;
BT_DBG("idx %u flags 0x%02x iv_index %u", idx, flags, iv_index);
BT_DBG("NetKey %s", bt_hex(key, 16));
if (BT_MESH_KEY_REFRESH(flags)) {
err = bt_mesh_subnet_set(idx, BT_MESH_KR_PHASE_2, NULL, key);
} else {
err = bt_mesh_subnet_set(idx, BT_MESH_KR_NORMAL, key, NULL);
}
if (err) {
BT_ERR("Failed creating subnet");
return err;
}
(void)memset(msg_cache, 0, sizeof(msg_cache));
msg_cache_next = 0U;
sub = &bt_mesh.sub[0];
sub->kr_flag = BT_MESH_KEY_REFRESH(flags);
if (sub->kr_flag) {
err = bt_mesh_net_keys_create(&sub->keys[1], key);
if (err) {
return -EIO;
}
sub->kr_phase = BT_MESH_KR_PHASE_2;
} else {
err = bt_mesh_net_keys_create(&sub->keys[0], key);
if (err) {
return -EIO;
}
}
sub->net_idx = idx;
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
} else {
sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
}
bt_mesh.iv_index = iv_index;
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS,
BT_MESH_IV_UPDATE(flags));
@ -475,91 +153,16 @@ int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
*/
bt_mesh.ivu_duration = BT_MESH_IVU_MIN_HOURS;
/* Make sure we have valid beacon data to be sent */
bt_mesh_net_beacon_update(sub);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing network information persistently");
bt_mesh_store_net();
bt_mesh_store_subnet(idx);
bt_mesh_store_iv(false);
}
return 0;
}
void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub)
{
int i;
BT_DBG("idx 0x%04x", sub->net_idx);
memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0]));
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing Updated NetKey persistently");
bt_mesh_store_subnet(sub);
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
if (key->net_idx != sub->net_idx || !key->updated) {
continue;
}
memcpy(&key->keys[0], &key->keys[1], sizeof(key->keys[0]));
key->updated = false;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing Updated AppKey persistently");
bt_mesh_store_app_key(key);
}
}
}
bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, uint8_t new_kr, bool new_key)
{
if (new_kr != sub->kr_flag && sub->kr_phase == BT_MESH_KR_NORMAL) {
BT_WARN("KR change in normal operation. Are we blacklisted?");
return false;
}
sub->kr_flag = new_kr;
if (sub->kr_flag) {
if (sub->kr_phase == BT_MESH_KR_PHASE_1) {
BT_DBG("Phase 1 -> Phase 2");
sub->kr_phase = BT_MESH_KR_PHASE_2;
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing krp phase persistently");
bt_mesh_store_subnet(sub);
}
return true;
}
} else {
switch (sub->kr_phase) {
case BT_MESH_KR_PHASE_1:
if (!new_key) {
/* Ignore */
break;
}
/* Upon receiving a Secure Network beacon with the KR flag set
* to 0 using the new NetKey in Phase 1, the node shall
* immediately transition to Phase 3, which effectively skips
* Phase 2.
*
* Intentional fall-through.
*/
__fallthrough;
case BT_MESH_KR_PHASE_2:
BT_DBG("KR Phase 0x%02x -> Normal", sub->kr_phase);
bt_mesh_net_revoke_keys(sub);
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
friend_cred_refresh(sub->net_idx);
}
sub->kr_phase = BT_MESH_KR_NORMAL;
return true;
}
}
return false;
}
#if defined(CONFIG_BT_MESH_IV_UPDATE_TEST)
void bt_mesh_iv_update_test(bool enable)
{
@ -581,29 +184,12 @@ bool bt_mesh_iv_update(void)
bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
}
bt_mesh_net_sec_update(NULL);
return atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS);
}
#endif /* CONFIG_BT_MESH_IV_UPDATE_TEST */
/* Used for sending immediate beacons to Friend queues and GATT clients */
void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub)
{
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friend_sec_update(sub ? sub->net_idx : BT_MESH_KEY_ANY);
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
bt_mesh_proxy_beacon_send(sub);
}
}
bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update)
{
int i;
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
/* We're currently in IV Update mode */
@ -685,12 +271,18 @@ do_update:
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
bt_mesh_net_beacon_update(&bt_mesh.sub[i]);
}
/* Notify other modules */
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND)) {
bt_mesh_friend_sec_update(BT_MESH_KEY_ANY);
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) &&
bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) {
bt_mesh_proxy_beacon_send(NULL);
}
bt_mesh_subnet_foreach(bt_mesh_beacon_update);
if (IS_ENABLED(CONFIG_BT_MESH_CDB)) {
bt_mesh_cdb_iv_update(iv_index, iv_update);
}
@ -715,7 +307,6 @@ uint32_t bt_mesh_next_seq(void)
bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY)) {
bt_mesh_beacon_ivu_initiator(true);
bt_mesh_net_iv_update(bt_mesh.iv_index + 1, true);
bt_mesh_net_sec_update(NULL);
}
return seq;
@ -744,7 +335,7 @@ static void bt_mesh_net_local(struct k_work *work)
.old_iv = (IVI(buf->data) != (bt_mesh.iv_index & 0x01)),
.ctl = CTL(buf->data),
.seq = SEQ(buf->data),
.new_key = sub->kr_flag,
.new_key = SUBNET_KEY_TX_IDX(sub),
.local_match = 1U,
.friend_match = 0U,
};
@ -757,26 +348,16 @@ static void bt_mesh_net_local(struct k_work *work)
}
}
static void net_tx_cred_get(struct bt_mesh_net_tx *tx, uint8_t *nid,
const uint8_t **enc, const uint8_t **priv)
static const struct bt_mesh_net_cred *net_tx_cred_get(struct bt_mesh_net_tx *tx)
{
if (!IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) || !tx->friend_cred) {
tx->friend_cred = 0U;
*nid = tx->sub->keys[tx->sub->kr_flag].nid;
*enc = tx->sub->keys[tx->sub->kr_flag].enc;
*priv = tx->sub->keys[tx->sub->kr_flag].privacy;
return;
#if defined(CONFIG_BT_MESH_LOW_POWER)
if (tx->friend_cred && bt_mesh_lpn_established()) {
return &bt_mesh.lpn.cred[SUBNET_KEY_TX_IDX(tx->sub)];
}
#endif
if (friend_cred_get(tx->sub, BT_MESH_ADDR_UNASSIGNED, nid, enc, priv)) {
BT_WARN("Falling back to master credentials");
tx->friend_cred = 0U;
*nid = tx->sub->keys[tx->sub->kr_flag].nid;
*enc = tx->sub->keys[tx->sub->kr_flag].enc;
*priv = tx->sub->keys[tx->sub->kr_flag].privacy;
}
tx->friend_cred = 0U;
return &tx->sub->keys[SUBNET_KEY_TX_IDX(tx->sub)].msg;
}
static int net_header_encode(struct bt_mesh_net_tx *tx, uint8_t nid,
@ -810,26 +391,33 @@ static int net_header_encode(struct bt_mesh_net_tx *tx, uint8_t nid,
return 0;
}
static int net_encrypt(struct net_buf_simple *buf,
const struct bt_mesh_net_cred *cred, uint32_t iv_index,
bool proxy)
{
int err;
err = bt_mesh_net_encrypt(cred->enc, buf, iv_index, proxy);
if (err) {
return err;
}
return bt_mesh_net_obfuscate(buf->data, iv_index, cred->privacy);
}
int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf,
bool proxy)
{
const uint8_t *enc, *priv;
uint8_t nid;
const struct bt_mesh_net_cred *cred;
int err;
net_tx_cred_get(tx, &nid, &enc, &priv);
err = net_header_encode(tx, nid, buf);
cred = net_tx_cred_get(tx);
err = net_header_encode(tx, cred->nid, buf);
if (err) {
return err;
}
err = bt_mesh_net_encrypt(enc, buf, BT_MESH_NET_IVI_TX, proxy);
if (err) {
return err;
}
return bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_TX, priv);
return net_encrypt(buf, cred, BT_MESH_NET_IVI_TX, proxy);
}
static int loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data,
@ -859,8 +447,7 @@ static int loopback(const struct bt_mesh_net_tx *tx, const uint8_t *data,
int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf,
const struct bt_mesh_send_cb *cb, void *cb_data)
{
const uint8_t *enc, *priv;
uint8_t nid;
const struct bt_mesh_net_cred *cred;
int err;
BT_DBG("src 0x%04x dst 0x%04x len %u headroom %zu tailroom %zu",
@ -873,8 +460,8 @@ int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf,
tx->ctx->send_ttl = bt_mesh_default_ttl_get();
}
net_tx_cred_get(tx, &nid, &enc, &priv);
err = net_header_encode(tx, nid, &buf->b);
cred = net_tx_cred_get(tx);
err = net_header_encode(tx, cred->nid, &buf->b);
if (err) {
goto done;
}
@ -905,12 +492,7 @@ int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf,
goto done;
}
err = bt_mesh_net_encrypt(enc, &buf->b, BT_MESH_NET_IVI_TX, false);
if (err) {
goto done;
}
err = bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_TX, priv);
err = net_encrypt(&buf->b, cred, BT_MESH_NET_IVI_TX, false);
if (err) {
goto done;
}
@ -956,186 +538,49 @@ void bt_mesh_net_loopback_clear(uint16_t net_idx)
bt_mesh.local_queue = new_list;
}
static bool auth_match(struct bt_mesh_subnet_keys *keys,
const uint8_t net_id[8], uint8_t flags,
uint32_t iv_index, const uint8_t auth[8])
static bool net_decrypt(struct bt_mesh_net_rx *rx, struct net_buf_simple *in,
struct net_buf_simple *out,
const struct bt_mesh_net_cred *cred)
{
uint8_t net_auth[8];
bool proxy = (rx->net_if == BT_MESH_NET_IF_PROXY_CFG);
if (memcmp(net_id, keys->net_id, 8)) {
if (NID(in->data) != cred->nid) {
return false;
}
bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, iv_index,
net_auth);
BT_DBG("NID 0x%02x", NID(in->data));
BT_DBG("IVI %u net->iv_index 0x%08x", IVI(in->data), bt_mesh.iv_index);
if (memcmp(auth, net_auth, 8)) {
BT_WARN("Authentication Value %s", bt_hex(auth, 8));
BT_WARN(" != %s", bt_hex(net_auth, 8));
rx->old_iv = (IVI(in->data) != (bt_mesh.iv_index & 0x01));
net_buf_simple_reset(out);
net_buf_simple_add_mem(out, in->data, in->len);
if (bt_mesh_net_obfuscate(out->data, BT_MESH_NET_IVI_RX(rx),
cred->privacy)) {
return false;
}
return true;
}
struct bt_mesh_subnet *bt_mesh_subnet_find(const uint8_t net_id[8], uint8_t flags,
uint32_t iv_index, const uint8_t auth[8],
bool *new_key)
{
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (auth_match(&sub->keys[0], net_id, flags, iv_index, auth)) {
*new_key = false;
return sub;
}
if (sub->kr_phase == BT_MESH_KR_NORMAL) {
continue;
}
if (auth_match(&sub->keys[1], net_id, flags, iv_index, auth)) {
*new_key = true;
return sub;
}
}
return NULL;
}
static int net_decrypt(struct bt_mesh_subnet *sub, const uint8_t *enc,
const uint8_t *priv, const uint8_t *data,
size_t data_len, struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
BT_DBG("IVI %u net->iv_index 0x%08x", IVI(data), bt_mesh.iv_index);
rx->old_iv = (IVI(data) != (bt_mesh.iv_index & 0x01));
net_buf_simple_reset(buf);
memcpy(net_buf_simple_add(buf, data_len), data, data_len);
if (bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_RX(rx), priv)) {
return -ENOENT;
}
rx->ctx.addr = SRC(buf->data);
rx->ctx.addr = SRC(out->data);
if (!BT_MESH_ADDR_IS_UNICAST(rx->ctx.addr)) {
BT_DBG("Ignoring non-unicast src addr 0x%04x", rx->ctx.addr);
return -EINVAL;
return false;
}
if (bt_mesh_elem_find(rx->ctx.addr)) {
BT_DBG("Dropping locally originated packet");
return -EBADMSG;
return false;
}
if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(buf)) {
if (rx->net_if == BT_MESH_NET_IF_ADV && msg_cache_match(out)) {
BT_DBG("Duplicate found in Network Message Cache");
return -EALREADY;
return false;
}
BT_DBG("src 0x%04x", rx->ctx.addr);
if (IS_ENABLED(CONFIG_BT_MESH_PROXY) &&
rx->net_if == BT_MESH_NET_IF_PROXY_CFG) {
return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx),
true);
}
return bt_mesh_net_decrypt(enc, buf, BT_MESH_NET_IVI_RX(rx), false);
}
static int friend_decrypt(struct bt_mesh_subnet *sub, const uint8_t *data,
size_t data_len, struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
int i;
BT_DBG("NID 0x%02x net_idx 0x%04x", NID(data), sub->net_idx);
for (i = 0; i < ARRAY_SIZE(friend_cred); i++) {
struct friend_cred *cred = &friend_cred[i];
if (cred->net_idx != sub->net_idx) {
continue;
}
if (NID(data) == cred->cred[0].nid &&
!net_decrypt(sub, cred->cred[0].enc, cred->cred[0].privacy,
data, data_len, rx, buf)) {
return 0;
}
if (sub->kr_phase == BT_MESH_KR_NORMAL) {
continue;
}
if (NID(data) == cred->cred[1].nid &&
!net_decrypt(sub, cred->cred[1].enc, cred->cred[1].privacy,
data, data_len, rx, buf)) {
rx->new_key = 1U;
return 0;
}
}
return -ENOENT;
}
static bool net_find_and_decrypt(const uint8_t *data, size_t data_len,
struct bt_mesh_net_rx *rx,
struct net_buf_simple *buf)
{
struct bt_mesh_subnet *sub;
int i;
BT_DBG("");
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if ((IS_ENABLED(CONFIG_BT_MESH_LOW_POWER) ||
IS_ENABLED(CONFIG_BT_MESH_FRIEND)) &&
!friend_decrypt(sub, data, data_len, rx, buf)) {
rx->friend_cred = 1U;
rx->ctx.net_idx = sub->net_idx;
rx->sub = sub;
return true;
}
if (NID(data) == sub->keys[0].nid &&
!net_decrypt(sub, sub->keys[0].enc, sub->keys[0].privacy,
data, data_len, rx, buf)) {
rx->ctx.net_idx = sub->net_idx;
rx->sub = sub;
return true;
}
if (sub->kr_phase == BT_MESH_KR_NORMAL) {
continue;
}
if (NID(data) == sub->keys[1].nid &&
!net_decrypt(sub, sub->keys[1].enc, sub->keys[1].privacy,
data, data_len, rx, buf)) {
rx->new_key = 1U;
rx->ctx.net_idx = sub->net_idx;
rx->sub = sub;
return true;
}
}
return false;
return bt_mesh_net_decrypt(cred->enc, out, BT_MESH_NET_IVI_RX(rx),
proxy) == 0;
}
/* Relaying from advertising to the advertising bearer should only happen
@ -1158,9 +603,9 @@ static bool relay_to_adv(enum bt_mesh_net_if net_if)
static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
struct bt_mesh_net_rx *rx)
{
const uint8_t *enc, *priv;
const struct bt_mesh_net_cred *cred;
struct net_buf *buf;
uint8_t nid, transmit;
uint8_t transmit;
if (rx->ctx.recv_ttl <= 1U) {
return;
@ -1197,32 +642,25 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf,
net_buf_add_mem(buf, sbuf->data, sbuf->len);
enc = rx->sub->keys[rx->sub->kr_flag].enc;
priv = rx->sub->keys[rx->sub->kr_flag].privacy;
nid = rx->sub->keys[rx->sub->kr_flag].nid;
cred = &rx->sub->keys[SUBNET_KEY_TX_IDX(rx->sub)].msg;
BT_DBG("Relaying packet. TTL is now %u", TTL(buf->data));
/* Update NID if RX or RX was with friend credentials */
if (rx->friend_cred) {
buf->data[0] &= 0x80; /* Clear everything except IVI */
buf->data[0] |= nid;
buf->data[0] |= cred->nid;
}
/* We re-encrypt and obfuscate using the received IVI rather than
* the normal TX IVI (which may be different) since the transport
* layer nonce includes the IVI.
*/
if (bt_mesh_net_encrypt(enc, &buf->b, BT_MESH_NET_IVI_RX(rx), false)) {
if (net_encrypt(&buf->b, cred, BT_MESH_NET_IVI_RX(rx), false)) {
BT_ERR("Re-encrypting failed");
goto done;
}
if (bt_mesh_net_obfuscate(buf->data, BT_MESH_NET_IVI_RX(rx), priv)) {
BT_ERR("Re-obfuscating failed");
goto done;
}
/* Sending to the GATT bearer should only happen if GATT Proxy
* is enabled.
*/
@ -1253,24 +691,24 @@ void bt_mesh_net_header_parse(struct net_buf_simple *buf,
rx->ctx.recv_dst = DST(buf->data);
}
int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if,
struct bt_mesh_net_rx *rx, struct net_buf_simple *buf)
int bt_mesh_net_decode(struct net_buf_simple *in, enum bt_mesh_net_if net_if,
struct bt_mesh_net_rx *rx, struct net_buf_simple *out)
{
if (data->len < BT_MESH_NET_MIN_PDU_LEN) {
BT_WARN("Dropping too short mesh packet (len %u)", data->len);
BT_WARN("%s", bt_hex(data->data, data->len));
if (in->len < BT_MESH_NET_MIN_PDU_LEN) {
BT_WARN("Dropping too short mesh packet (len %u)", in->len);
BT_WARN("%s", bt_hex(in->data, in->len));
return -EINVAL;
}
if (net_if == BT_MESH_NET_IF_ADV && check_dup(data)) {
if (net_if == BT_MESH_NET_IF_ADV && check_dup(in)) {
return -EINVAL;
}
BT_DBG("%u bytes: %s", data->len, bt_hex(data->data, data->len));
BT_DBG("%u bytes: %s", in->len, bt_hex(in->data, in->len));
rx->net_if = net_if;
if (!net_find_and_decrypt(data->data, data->len, rx, buf)) {
if (!bt_mesh_net_cred_find(rx, in, out, net_decrypt)) {
BT_DBG("Unable to find matching net for packet");
return -ENOENT;
}
@ -1278,7 +716,7 @@ int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if,
/* Initialize AppIdx to a sane value */
rx->ctx.app_idx = BT_MESH_KEY_UNUSED;
rx->ctx.recv_ttl = TTL(buf->data);
rx->ctx.recv_ttl = TTL(out->data);
/* Default to responding with TTL 0 for non-routed messages */
if (rx->ctx.recv_ttl == 0U) {
@ -1287,11 +725,11 @@ int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if,
rx->ctx.send_ttl = BT_MESH_TTL_DEFAULT;
}
rx->ctl = CTL(buf->data);
rx->seq = SEQ(buf->data);
rx->ctx.recv_dst = DST(buf->data);
rx->ctl = CTL(out->data);
rx->seq = SEQ(out->data);
rx->ctx.recv_dst = DST(out->data);
BT_DBG("Decryption successful. Payload len %u", buf->len);
BT_DBG("Decryption successful. Payload len %u", out->len);
if (net_if != BT_MESH_NET_IF_PROXY_CFG &&
rx->ctx.recv_dst == BT_MESH_ADDR_UNASSIGNED) {
@ -1301,7 +739,7 @@ int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if,
BT_DBG("src 0x%04x dst 0x%04x ttl %u", rx->ctx.addr, rx->ctx.recv_dst,
rx->ctx.recv_ttl);
BT_DBG("PDU: %s", bt_hex(buf->data, buf->len));
BT_DBG("PDU: %s", bt_hex(out->data, out->len));
msg_cache_add(rx);
@ -1417,10 +855,10 @@ void bt_mesh_net_start(void)
}
if (IS_ENABLED(CONFIG_BT_MESH_PROV)) {
uint16_t net_idx = bt_mesh.sub[0].net_idx;
struct bt_mesh_subnet *sub = bt_mesh_subnet_next(NULL);
uint16_t addr = bt_mesh_primary_addr();
bt_mesh_prov_complete(net_idx, addr);
bt_mesh_prov_complete(sub->net_idx, addr);
}
}

View file

@ -6,13 +6,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
#define BT_MESH_NET_FLAG_KR BIT(0)
#define BT_MESH_NET_FLAG_IVU BIT(1)
#define BT_MESH_KR_NORMAL 0x00
#define BT_MESH_KR_PHASE_1 0x01
#define BT_MESH_KR_PHASE_2 0x02
#define BT_MESH_KR_PHASE_3 0x03
#include "subnet.h"
#define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01)
#define BT_MESH_KEY_REFRESH(flags) (flags & 0x01)
@ -23,15 +17,7 @@
CONFIG_BT_MESH_IVU_DIVIDER)
#define BT_MESH_IVU_TIMEOUT K_HOURS(BT_MESH_IVU_HOURS)
struct bt_mesh_app_key {
uint16_t net_idx;
uint16_t app_idx;
bool updated;
struct bt_mesh_app_keys {
uint8_t id;
uint8_t val[16];
} keys[2];
};
struct bt_mesh_net_cred;
struct bt_mesh_node {
uint16_t addr;
@ -40,40 +26,6 @@ struct bt_mesh_node {
uint8_t num_elem;
};
struct bt_mesh_subnet {
uint32_t beacon_sent; /* Timestamp of last sent beacon */
uint8_t beacons_last; /* Number of beacons during last
* observation window
*/
uint8_t beacons_cur; /* Number of beaconds observed during
* currently ongoing window.
*/
uint8_t beacon_cache[21]; /* Cached last authenticated beacon */
uint16_t net_idx; /* NetKeyIndex */
bool kr_flag; /* Key Refresh Flag */
uint8_t kr_phase; /* Key Refresh Phase */
uint8_t node_id; /* Node Identity State */
uint32_t node_id_start; /* Node Identity started timestamp */
uint8_t auth[8]; /* Beacon Authentication Value */
struct bt_mesh_subnet_keys {
uint8_t net[16]; /* NetKey */
uint8_t nid; /* NID */
uint8_t enc[16]; /* EncKey */
uint8_t net_id[8]; /* Network ID */
#if defined(CONFIG_BT_MESH_GATT_PROXY)
uint8_t identity[16]; /* IdentityKey */
#endif
uint8_t privacy[16]; /* PrivacyKey */
uint8_t beacon[16]; /* BeaconKey */
} keys[2];
};
#if defined(CONFIG_BT_MESH_FRIEND)
#define FRIEND_SEG_RX CONFIG_BT_MESH_FRIEND_SEG_RX
#define FRIEND_SUB_LIST_SIZE CONFIG_BT_MESH_FRIEND_SUB_LIST_SIZE
@ -89,14 +41,15 @@ struct bt_mesh_friend {
send_last:1,
pending_req:1,
pending_buf:1,
valid:1,
established:1;
int32_t poll_to;
uint8_t num_elem;
uint16_t lpn_counter;
uint16_t counter;
uint16_t net_idx;
struct bt_mesh_subnet *subnet;
struct bt_mesh_net_cred cred[2];
uint16_t sub_list[FRIEND_SUB_LIST_SIZE];
@ -173,8 +126,11 @@ struct bt_mesh_lpn {
/* Friend Queue Size */
uint8_t queue_size;
/* FriendCounter */
uint16_t frnd_counter;
/* LPNCounter */
uint16_t counter;
uint16_t lpn_counter;
/* Previous Friend of this LPN */
uint16_t old_friend;
@ -188,6 +144,10 @@ struct bt_mesh_lpn {
/* Subscribed groups */
uint16_t groups[LPN_GROUPS];
struct bt_mesh_subnet *sub;
struct bt_mesh_net_cred cred[2];
/* Bit fields for tracking which groups the Friend knows about */
ATOMIC_DEFINE(added, LPN_GROUPS);
ATOMIC_DEFINE(pending, LPN_GROUPS);
@ -244,10 +204,6 @@ struct bt_mesh_net {
struct k_delayed_work ivu_timer;
uint8_t dev_key[16];
struct bt_mesh_app_key app_keys[CONFIG_BT_MESH_APP_KEY_COUNT];
struct bt_mesh_subnet sub[CONFIG_BT_MESH_SUBNET_COUNT];
};
/* Network interface */
@ -293,38 +249,19 @@ extern struct bt_mesh_net bt_mesh;
#define BT_MESH_NET_HDR_LEN 9
int bt_mesh_net_keys_create(struct bt_mesh_subnet_keys *keys,
const uint8_t key[16]);
int bt_mesh_net_create(uint16_t idx, uint8_t flags, const uint8_t key[16],
uint32_t iv_index);
uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
bool bt_mesh_kr_update(struct bt_mesh_subnet *sub, uint8_t new_kr, bool new_key);
void bt_mesh_net_revoke_keys(struct bt_mesh_subnet *sub);
int bt_mesh_net_beacon_update(struct bt_mesh_subnet *sub);
bool bt_mesh_net_iv_update(uint32_t iv_index, bool iv_update);
void bt_mesh_net_sec_update(struct bt_mesh_subnet *sub);
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx);
struct bt_mesh_subnet *bt_mesh_subnet_find(const uint8_t net_id[8], uint8_t flags,
uint32_t iv_index, const uint8_t auth[8],
bool *new_key);
int bt_mesh_net_encode(struct bt_mesh_net_tx *tx, struct net_buf_simple *buf,
bool proxy);
int bt_mesh_net_send(struct bt_mesh_net_tx *tx, struct net_buf *buf,
const struct bt_mesh_send_cb *cb, void *cb_data);
int bt_mesh_net_decode(struct net_buf_simple *data, enum bt_mesh_net_if net_if,
struct bt_mesh_net_rx *rx, struct net_buf_simple *buf);
int bt_mesh_net_decode(struct net_buf_simple *in, enum bt_mesh_net_if net_if,
struct bt_mesh_net_rx *rx, struct net_buf_simple *out);
void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi,
enum bt_mesh_net_if net_if);
@ -339,31 +276,6 @@ void bt_mesh_net_init(void);
void bt_mesh_net_header_parse(struct net_buf_simple *buf,
struct bt_mesh_net_rx *rx);
/* Friendship Credential Management */
struct friend_cred {
uint16_t net_idx;
uint16_t addr;
uint16_t lpn_counter;
uint16_t frnd_counter;
struct {
uint8_t nid; /* NID */
uint8_t enc[16]; /* EncKey */
uint8_t privacy[16]; /* PrivacyKey */
} cred[2];
};
int friend_cred_get(struct bt_mesh_subnet *sub, uint16_t addr, uint8_t *nid,
const uint8_t **enc, const uint8_t **priv);
int friend_cred_set(struct friend_cred *cred, uint8_t idx, const uint8_t net_key[16]);
void friend_cred_refresh(uint16_t net_idx);
int friend_cred_update(struct bt_mesh_subnet *sub);
struct friend_cred *friend_cred_create(struct bt_mesh_subnet *sub, uint16_t addr,
uint16_t lpn_counter, uint16_t frnd_counter);
void friend_cred_clear(struct friend_cred *cred);
int friend_cred_del(uint16_t net_idx, uint16_t addr);
static inline void send_cb_finalize(const struct bt_mesh_send_cb *cb,
void *cb_data)
{

View file

@ -144,7 +144,7 @@ static void proxy_sar_timeout(struct k_work *work)
#if defined(CONFIG_BT_MESH_GATT_PROXY)
/* Next subnet in queue to be advertised */
static int next_idx;
static struct bt_mesh_subnet *beacon_sub;
static int proxy_segment_and_send(struct bt_conn *conn, uint8_t type,
struct net_buf_simple *msg);
@ -341,20 +341,20 @@ static int beacon_send(struct bt_conn *conn, struct bt_mesh_subnet *sub)
return proxy_segment_and_send(conn, BT_MESH_PROXY_BEACON, &buf);
}
static int send_beacon_cb(struct bt_mesh_subnet *sub, void *cb_data)
{
struct bt_mesh_proxy_client *client = cb_data;
return beacon_send(client->conn, sub);
}
static void proxy_send_beacons(struct k_work *work)
{
struct bt_mesh_proxy_client *client;
int i;
client = CONTAINER_OF(work, struct bt_mesh_proxy_client, send_beacons);
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx != BT_MESH_KEY_UNUSED) {
beacon_send(client->conn, sub);
}
}
(void)bt_mesh_subnet_find(send_beacon_cb, client);
}
void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
@ -363,12 +363,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
if (!sub) {
/* NULL means we send on all subnets */
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
if (bt_mesh.sub[i].net_idx != BT_MESH_KEY_UNUSED) {
bt_mesh_proxy_beacon_send(&bt_mesh.sub[i]);
}
}
bt_mesh_subnet_foreach(bt_mesh_proxy_beacon_send);
return;
}
@ -379,13 +374,18 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub)
}
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
static void node_id_start(struct bt_mesh_subnet *sub)
{
sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING;
sub->node_id_start = k_uptime_get_32();
}
void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub)
{
node_id_start(sub);
/* Prioritize the recently enabled subnet */
next_idx = sub - bt_mesh.sub;
beacon_sub = sub;
}
void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
@ -396,30 +396,13 @@ void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub)
int bt_mesh_proxy_identity_enable(void)
{
int i, count = 0;
BT_DBG("");
if (!bt_mesh_is_provisioned()) {
return -EAGAIN;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (sub->node_id == BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) {
continue;
}
bt_mesh_proxy_identity_start(sub);
count++;
}
if (count) {
if (bt_mesh_subnet_foreach(node_id_start)) {
bt_mesh_adv_update();
}
@ -1094,7 +1077,8 @@ static int node_id_adv(struct bt_mesh_subnet *sub)
memcpy(tmp + 6, proxy_svc_data + 11, 8);
sys_put_be16(bt_mesh_primary_addr(), tmp + 14);
err = bt_encrypt_be(sub->keys[sub->kr_flag].identity, tmp, tmp);
err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp,
tmp);
if (err) {
return err;
}
@ -1122,9 +1106,9 @@ static int net_id_adv(struct bt_mesh_subnet *sub)
proxy_svc_data[2] = ID_TYPE_NET;
BT_DBG("Advertising with NetId %s",
bt_hex(sub->keys[sub->kr_flag].net_id, 8));
bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8));
memcpy(proxy_svc_data + 3, sub->keys[sub->kr_flag].net_id, 8);
memcpy(proxy_svc_data + 3, sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8);
err = bt_le_adv_start(&slow_adv_param, net_id_ad,
ARRAY_SIZE(net_id_ad), NULL, 0);
@ -1150,32 +1134,46 @@ static bool advertise_subnet(struct bt_mesh_subnet *sub)
static struct bt_mesh_subnet *next_sub(void)
{
int i;
struct bt_mesh_subnet *sub = NULL;
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub;
sub = &bt_mesh.sub[(i + next_idx) % ARRAY_SIZE(bt_mesh.sub)];
if (advertise_subnet(sub)) {
next_idx = (next_idx + 1) % ARRAY_SIZE(bt_mesh.sub);
return sub;
if (!beacon_sub) {
beacon_sub = bt_mesh_subnet_next(NULL);
if (!beacon_sub) {
/* No valid subnets */
return NULL;
}
}
sub = beacon_sub;
do {
if (advertise_subnet(sub)) {
beacon_sub = sub;
return sub;
}
sub = bt_mesh_subnet_next(sub);
} while (sub != beacon_sub);
/* No subnets to advertise on */
return NULL;
}
static int sub_count_cb(struct bt_mesh_subnet *sub, void *cb_data)
{
int *count = cb_data;
if (advertise_subnet(sub)) {
(*count)++;
}
return 0;
}
static int sub_count(void)
{
int i, count = 0;
int count = 0;
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
if (advertise_subnet(sub)) {
count++;
}
}
(void)bt_mesh_subnet_find(sub_count_cb, &count);
return count;
}
@ -1192,6 +1190,7 @@ static k_timeout_t gatt_proxy_advertise(struct bt_mesh_subnet *sub)
return K_FOREVER;
}
sub = beacon_sub ? beacon_sub : bt_mesh_subnet_next(beacon_sub);
if (!sub) {
BT_WARN("No subnets to advertise on");
return K_FOREVER;
@ -1235,6 +1234,8 @@ static k_timeout_t gatt_proxy_advertise(struct bt_mesh_subnet *sub)
BT_DBG("Advertising %d ms for net_idx 0x%04x", remaining, sub->net_idx);
beacon_sub = bt_mesh_subnet_next(beacon_sub);
return SYS_TIMEOUT_MS(remaining);
}
#endif /* GATT_PROXY */
@ -1347,6 +1348,21 @@ void bt_mesh_proxy_adv_stop(void)
}
}
#if defined(CONFIG_BT_MESH_GATT_PROXY)
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
if (evt == BT_MESH_KEY_DELETED) {
if (sub == beacon_sub) {
beacon_sub = NULL;
}
} else {
bt_mesh_proxy_beacon_send(sub);
}
}
BT_MESH_SUBNET_CB_DEFINE(subnet_evt);
#endif
static struct bt_conn_cb conn_callbacks = {
.connected = proxy_connected,
.disconnected = proxy_disconnected,

View file

@ -25,6 +25,8 @@
#include "common/log.h"
#include "mesh.h"
#include "subnet.h"
#include "app_keys.h"
#include "net.h"
#include "crypto.h"
#include "rpl.h"
@ -334,9 +336,8 @@ static int rpl_set(const char *name, size_t len_rd,
static int net_key_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct bt_mesh_subnet *sub;
struct net_key_val key;
int i, err;
int err;
uint16_t net_idx;
if (!name) {
@ -345,64 +346,22 @@ static int net_key_set(const char *name, size_t len_rd,
}
net_idx = strtol(name, NULL, 16);
sub = bt_mesh_subnet_get(net_idx);
if (len_rd == 0) {
BT_DBG("val (null)");
if (!sub) {
BT_ERR("No subnet with NetKeyIndex 0x%03x", net_idx);
return -ENOENT;
}
BT_DBG("Deleting NetKeyIndex 0x%03x", net_idx);
bt_mesh_subnet_del(sub, false);
return 0;
}
err = mesh_x_set(read_cb, cb_arg, &key, sizeof(key));
if (err) {
BT_ERR("Failed to set \'net-key\'");
return err;
}
if (sub) {
BT_DBG("Updating existing NetKeyIndex 0x%03x", net_idx);
sub->kr_flag = key.kr_flag;
sub->kr_phase = key.kr_phase;
memcpy(sub->keys[0].net, &key.val[0], 16);
memcpy(sub->keys[1].net, &key.val[1], 16);
return 0;
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
if (bt_mesh.sub[i].net_idx == BT_MESH_KEY_UNUSED) {
sub = &bt_mesh.sub[i];
break;
}
}
if (!sub) {
BT_ERR("No space to allocate a new subnet");
return -ENOMEM;
}
sub->net_idx = net_idx;
sub->kr_flag = key.kr_flag;
sub->kr_phase = key.kr_phase;
memcpy(sub->keys[0].net, &key.val[0], 16);
memcpy(sub->keys[1].net, &key.val[1], 16);
BT_DBG("NetKeyIndex 0x%03x recovered from storage", net_idx);
return 0;
return bt_mesh_subnet_set(
net_idx, key.kr_phase, key.val[0],
(key.kr_phase != BT_MESH_KR_NORMAL) ? key.val[1] : NULL);
}
static int app_key_set(const char *name, size_t len_rd,
settings_read_cb read_cb, void *cb_arg)
{
struct bt_mesh_app_key *app;
struct app_key_val key;
uint16_t app_idx;
int err;
@ -414,43 +373,22 @@ static int app_key_set(const char *name, size_t len_rd,
app_idx = strtol(name, NULL, 16);
if (len_rd == 0) {
BT_DBG("val (null)");
BT_DBG("Deleting AppKeyIndex 0x%03x", app_idx);
app = bt_mesh_app_key_find(app_idx);
if (app) {
bt_mesh_app_key_del(app, false);
}
if (!len_rd) {
return 0;
}
err = mesh_x_set(read_cb, cb_arg, &key, sizeof(key));
len_rd = read_cb(cb_arg, &key, sizeof(key));
if (len_rd != sizeof(key)) {
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;
}
app = bt_mesh_app_key_find(app_idx);
if (!app) {
app = bt_mesh_app_key_alloc(app_idx);
}
if (!app) {
BT_ERR("No space for a new app key");
return -ENOMEM;
}
app->net_idx = key.net_idx;
app->app_idx = app_idx;
app->updated = key.updated;
memcpy(app->keys[0].val, key.val[0], 16);
memcpy(app->keys[1].val, key.val[1], 16);
bt_mesh_app_id(app->keys[0].val, &app->keys[0].id);
bt_mesh_app_id(app->keys[1].val, &app->keys[1].id);
BT_DBG("AppKeyIndex 0x%03x recovered from storage", app_idx);
return 0;
@ -1042,37 +980,6 @@ static int mesh_set(const char *name, size_t len_rd,
return -ENOENT;
}
static int subnet_init(struct bt_mesh_subnet *sub)
{
int err;
err = bt_mesh_net_keys_create(&sub->keys[0], sub->keys[0].net);
if (err) {
BT_ERR("Unable to generate keys for subnet");
return -EIO;
}
if (sub->kr_phase != BT_MESH_KR_NORMAL) {
err = bt_mesh_net_keys_create(&sub->keys[1], sub->keys[1].net);
if (err) {
BT_ERR("Unable to generate keys for subnet");
(void)memset(&sub->keys[0], 0, sizeof(sub->keys[0]));
return -EIO;
}
}
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
} else {
sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
}
/* Make sure we have valid beacon data to be sent */
bt_mesh_net_beacon_update(sub);
return 0;
}
static void commit_mod(struct bt_mesh_model *mod, struct bt_mesh_elem *elem,
bool vnd, bool primary, void *user_data)
{
@ -1101,11 +1008,8 @@ static int mesh_commit(void)
{
struct bt_mesh_hb_pub *hb_pub;
struct bt_mesh_cfg_srv *cfg;
int i;
BT_DBG("sub[0].net_idx 0x%03x", bt_mesh.sub[0].net_idx);
if (bt_mesh.sub[0].net_idx == BT_MESH_KEY_UNUSED) {
if (!bt_mesh_subnet_next(NULL)) {
/* Nothing to do since we're not yet provisioned */
return 0;
}
@ -1114,20 +1018,6 @@ static int mesh_commit(void)
bt_mesh_proxy_prov_disable(true);
}
for (i = 0; i < ARRAY_SIZE(bt_mesh.sub); i++) {
struct bt_mesh_subnet *sub = &bt_mesh.sub[i];
int err;
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
err = subnet_init(sub);
if (err) {
BT_ERR("Failed to init subnet 0x%03x", sub->net_idx);
}
}
if (bt_mesh.ivu_duration < BT_MESH_IVU_MIN_HOURS) {
k_delayed_work_submit(&bt_mesh.ivu_timer, BT_MESH_IVU_TIMEOUT);
}
@ -1452,22 +1342,28 @@ static void clear_net_key(uint16_t net_idx)
}
}
static void store_net_key(struct bt_mesh_subnet *sub)
static void store_subnet(uint16_t net_idx)
{
const struct bt_mesh_subnet *sub;
struct net_key_val key;
char path[20];
int err;
BT_DBG("NetKeyIndex 0x%03x NetKey %s", sub->net_idx,
bt_hex(sub->keys[0].net, 16));
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
BT_WARN("NetKeyIndex 0x%03x not found", net_idx);
return;
}
BT_DBG("NetKeyIndex 0x%03x", net_idx);
snprintk(path, sizeof(path), "bt/mesh/NetKey/%x", net_idx);
memcpy(&key.val[0], sub->keys[0].net, 16);
memcpy(&key.val[1], sub->keys[1].net, 16);
key.kr_flag = sub->kr_flag;
key.kr_flag = 0U; /* Deprecated */
key.kr_phase = sub->kr_phase;
snprintk(path, sizeof(path), "bt/mesh/NetKey/%x", sub->net_idx);
err = settings_save_one(path, &key, sizeof(key));
if (err) {
BT_ERR("Failed to store NetKey value");
@ -1476,19 +1372,27 @@ static void store_net_key(struct bt_mesh_subnet *sub)
}
}
static void store_app_key(struct bt_mesh_app_key *app)
static void store_app(uint16_t app_idx)
{
const struct bt_mesh_app_key *app;
struct app_key_val key;
char path[20];
int err;
key.net_idx = app->net_idx;
key.updated = app->updated;
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app_idx);
app = bt_mesh_app_key_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);
snprintk(path, sizeof(path), "bt/mesh/AppKey/%x", app->app_idx);
err = settings_save_one(path, &key, sizeof(key));
if (err) {
BT_ERR("Failed to store AppKey %s value", log_strdup(path));
@ -1516,26 +1420,9 @@ static void store_pending_keys(void)
}
} else {
if (update->app_key) {
struct bt_mesh_app_key *key;
key = bt_mesh_app_key_find(update->key_idx);
if (key) {
store_app_key(key);
} else {
BT_WARN("AppKeyIndex 0x%03x not found",
update->key_idx);
}
store_app(update->key_idx);
} else {
struct bt_mesh_subnet *sub;
sub = bt_mesh_subnet_get(update->key_idx);
if (sub) {
store_net_key(sub);
} else {
BT_WARN("NetKeyIndex 0x%03x not found",
update->key_idx);
}
store_subnet(update->key_idx);
}
}
@ -2066,13 +1953,13 @@ static struct key_update *key_update_find(bool app_key, uint16_t key_idx,
return match;
}
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub)
void bt_mesh_store_subnet(uint16_t net_idx)
{
struct key_update *update, *free_slot;
BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
BT_DBG("NetKeyIndex 0x%03x", net_idx);
update = key_update_find(false, sub->net_idx, &free_slot);
update = key_update_find(false, net_idx, &free_slot);
if (update) {
update->clear = 0U;
schedule_store(BT_MESH_KEYS_PENDING);
@ -2080,25 +1967,25 @@ void bt_mesh_store_subnet(struct bt_mesh_subnet *sub)
}
if (!free_slot) {
store_net_key(sub);
store_subnet(net_idx);
return;
}
free_slot->valid = 1U;
free_slot->key_idx = sub->net_idx;
free_slot->key_idx = net_idx;
free_slot->app_key = 0U;
free_slot->clear = 0U;
schedule_store(BT_MESH_KEYS_PENDING);
}
void bt_mesh_store_app_key(struct bt_mesh_app_key *key)
void bt_mesh_store_app_key(uint16_t app_idx)
{
struct key_update *update, *free_slot;
BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
BT_DBG("AppKeyIndex 0x%03x", app_idx);
update = key_update_find(true, key->app_idx, &free_slot);
update = key_update_find(true, app_idx, &free_slot);
if (update) {
update->clear = 0U;
schedule_store(BT_MESH_KEYS_PENDING);
@ -2106,12 +1993,12 @@ void bt_mesh_store_app_key(struct bt_mesh_app_key *key)
}
if (!free_slot) {
store_app_key(key);
store_app(app_idx);
return;
}
free_slot->valid = 1U;
free_slot->key_idx = key->app_idx;
free_slot->key_idx = app_idx;
free_slot->app_key = 1U;
free_slot->clear = 0U;
@ -2135,13 +2022,13 @@ void bt_mesh_clear_net(void)
schedule_store(BT_MESH_CFG_PENDING);
}
void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub)
void bt_mesh_clear_subnet(uint16_t net_idx)
{
struct key_update *update, *free_slot;
BT_DBG("NetKeyIndex 0x%03x", sub->net_idx);
BT_DBG("NetKeyIndex 0x%03x", net_idx);
update = key_update_find(false, sub->net_idx, &free_slot);
update = key_update_find(false, net_idx, &free_slot);
if (update) {
update->clear = 1U;
schedule_store(BT_MESH_KEYS_PENDING);
@ -2149,25 +2036,25 @@ void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub)
}
if (!free_slot) {
clear_net_key(sub->net_idx);
clear_net_key(net_idx);
return;
}
free_slot->valid = 1U;
free_slot->key_idx = sub->net_idx;
free_slot->key_idx = net_idx;
free_slot->app_key = 0U;
free_slot->clear = 1U;
schedule_store(BT_MESH_KEYS_PENDING);
}
void bt_mesh_clear_app_key(struct bt_mesh_app_key *key)
void bt_mesh_clear_app_key(uint16_t app_idx)
{
struct key_update *update, *free_slot;
BT_DBG("AppKeyIndex 0x%03x", key->app_idx);
BT_DBG("AppKeyIndex 0x%03x", app_idx);
update = key_update_find(true, key->app_idx, &free_slot);
update = key_update_find(true, app_idx, &free_slot);
if (update) {
update->clear = 1U;
schedule_store(BT_MESH_KEYS_PENDING);
@ -2175,12 +2062,12 @@ void bt_mesh_clear_app_key(struct bt_mesh_app_key *key)
}
if (!free_slot) {
clear_app_key(key->app_idx);
clear_app_key(app_idx);
return;
}
free_slot->valid = 1U;
free_slot->key_idx = key->app_idx;
free_slot->key_idx = app_idx;
free_slot->app_key = 1U;
free_slot->clear = 1U;

View file

@ -8,8 +8,8 @@ void bt_mesh_store_net(void);
void bt_mesh_store_iv(bool only_duration);
void bt_mesh_store_seq(void);
void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
void bt_mesh_store_app_key(struct bt_mesh_app_key *key);
void bt_mesh_store_subnet(uint16_t net_idx);
void bt_mesh_store_app_key(uint16_t app_idx);
void bt_mesh_store_hb_pub(void);
void bt_mesh_store_cfg(void);
void bt_mesh_store_mod_bind(struct bt_mesh_model *mod);
@ -22,8 +22,8 @@ void bt_mesh_store_cdb_subnet(const struct bt_mesh_cdb_subnet *sub);
void bt_mesh_store_cdb_app_key(const struct bt_mesh_cdb_app_key *app);
void bt_mesh_clear_net(void);
void bt_mesh_clear_subnet(struct bt_mesh_subnet *sub);
void bt_mesh_clear_app_key(struct bt_mesh_app_key *key);
void bt_mesh_clear_subnet(uint16_t net_idx);
void bt_mesh_clear_app_key(uint16_t app_idx);
void bt_mesh_clear_rpl(void);
void bt_mesh_clear_cdb_node(struct bt_mesh_cdb_node *node);
void bt_mesh_clear_cdb_subnet(struct bt_mesh_cdb_subnet *sub);

View file

@ -703,8 +703,6 @@ static int cmd_net_send(const struct shell *shell, size_t argc, char *argv[])
struct bt_mesh_net_tx tx = {
.ctx = &ctx,
.src = net.local,
.xmit = bt_mesh_net_transmit_get(),
.sub = bt_mesh_subnet_get(net.net_idx),
};
size_t len;
int err;
@ -713,12 +711,6 @@ static int cmd_net_send(const struct shell *shell, size_t argc, char *argv[])
return -EINVAL;
}
if (!tx.sub) {
shell_print(shell, "No matching subnet for NetKey Index 0x%04x",
net.net_idx);
return 0;
}
len = hex2bin(argv[1], strlen(argv[1]),
msg.data, net_buf_simple_tailroom(&msg) - 4);
net_buf_simple_add(&msg, len);

View file

@ -0,0 +1,660 @@
/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr.h>
#include <string.h>
#include <errno.h>
#include <stdbool.h>
#include <sys/atomic.h>
#include <sys/util.h>
#include <sys/byteorder.h>
#include <net/buf.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/conn.h>
#include <bluetooth/mesh.h>
#define BT_DBG_ENABLED IS_ENABLED(CONFIG_BT_MESH_DEBUG_KEYS)
#define LOG_MODULE_NAME bt_mesh_net_keys
#include "common/log.h"
#include "crypto.h"
#include "adv.h"
#include "mesh.h"
#include "net.h"
#include "lpn.h"
#include "friend.h"
#include "proxy.h"
#include "transport.h"
#include "access.h"
#include "foundation.h"
#include "beacon.h"
#include "rpl.h"
#include "settings.h"
#include "prov.h"
static struct bt_mesh_subnet subnets[CONFIG_BT_MESH_SUBNET_COUNT] = {
[0 ... (CONFIG_BT_MESH_SUBNET_COUNT - 1)] = {
.net_idx = BT_MESH_KEY_UNUSED,
},
};
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
{
Z_STRUCT_SECTION_FOREACH(bt_mesh_subnet_cb, cb) {
cb->evt_handler(sub, evt);
}
}
uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub)
{
uint8_t flags = 0x00;
if (sub && (sub->kr_phase == BT_MESH_KR_PHASE_2)) {
flags |= BT_MESH_NET_FLAG_KR;
}
if (atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_IN_PROGRESS)) {
flags |= BT_MESH_NET_FLAG_IVU;
}
return flags;
}
static void key_refresh(struct bt_mesh_subnet *sub, uint8_t new_phase)
{
BT_DBG("Phase 0x%02x -> 0x%02x", sub->kr_phase, new_phase);
switch (new_phase) {
/* Added second set of keys */
case BT_MESH_KR_PHASE_1:
sub->kr_phase = new_phase;
subnet_evt(sub, BT_MESH_KEY_UPDATED);
break;
/* Now using new keys for TX */
case BT_MESH_KR_PHASE_2:
sub->kr_phase = new_phase;
subnet_evt(sub, BT_MESH_KEY_SWAPPED);
break;
/* Revoking keys */
case BT_MESH_KR_PHASE_3:
case BT_MESH_KR_NORMAL:
sub->kr_phase = BT_MESH_KR_NORMAL;
memcpy(&sub->keys[0], &sub->keys[1], sizeof(sub->keys[0]));
sub->keys[1].valid = 0U;
subnet_evt(sub, BT_MESH_KEY_REVOKED);
break;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing Updated NetKey persistently");
bt_mesh_store_subnet(sub->net_idx);
}
}
void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key)
{
if (!new_key) {
return;
}
if (sub->kr_phase == BT_MESH_KR_PHASE_1) {
/* Bluetooth Mesh Profile Specification Section 3.10.4.1:
* Can skip phase 2 if we get KR=0 on new key.
*/
key_refresh(sub, (kr_flag ? BT_MESH_KR_PHASE_2 :
BT_MESH_KR_PHASE_3));
} else if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !kr_flag) {
key_refresh(sub, BT_MESH_KR_PHASE_3);
}
}
static struct bt_mesh_subnet *subnet_alloc(uint16_t net_idx)
{
struct bt_mesh_subnet *sub = NULL;
for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
/* Check for already existing subnet */
if (subnets[i].net_idx == net_idx) {
return &subnets[i];
}
if (!sub && subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
sub = &subnets[i];
}
}
return sub;
}
static void subnet_del(struct bt_mesh_subnet *sub)
{
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_clear_subnet(sub->net_idx);
}
bt_mesh_net_loopback_clear(sub->net_idx);
subnet_evt(sub, BT_MESH_KEY_DELETED);
(void)memset(sub, 0, sizeof(*sub));
sub->net_idx = BT_MESH_KEY_UNUSED;
}
static int msg_cred_create(struct bt_mesh_net_cred *cred, const uint8_t *p,
size_t p_len, const uint8_t key[16])
{
return bt_mesh_k2(key, p, p_len, &cred->nid, cred->enc, cred->privacy);
}
static int net_keys_create(struct bt_mesh_subnet_keys *keys,
const uint8_t key[16])
{
uint8_t p = 0;
int err;
err = msg_cred_create(&keys->msg, &p, 1, key);
if (err) {
BT_ERR("Unable to generate NID, EncKey & PrivacyKey");
return err;
}
memcpy(keys->net, key, 16);
BT_DBG("NID 0x%02x EncKey %s", keys->msg.nid,
bt_hex(keys->msg.enc, 16));
BT_DBG("PrivacyKey %s", bt_hex(keys->msg.privacy, 16));
err = bt_mesh_k3(key, keys->net_id);
if (err) {
BT_ERR("Unable to generate Net ID");
return err;
}
BT_DBG("NetID %s", bt_hex(keys->net_id, 8));
#if defined(CONFIG_BT_MESH_GATT_PROXY)
err = bt_mesh_identity_key(key, keys->identity);
if (err) {
BT_ERR("Unable to generate IdentityKey");
return err;
}
BT_DBG("IdentityKey %s", bt_hex(keys->identity, 16));
#endif /* GATT_PROXY */
err = bt_mesh_beacon_key(key, keys->beacon);
if (err) {
BT_ERR("Unable to generate beacon key");
return err;
}
BT_DBG("BeaconKey %s", bt_hex(keys->beacon, 16));
keys->valid = 1U;
return 0;
}
uint8_t bt_mesh_subnet_add(uint16_t net_idx, const uint8_t key[16])
{
struct bt_mesh_subnet *sub = NULL;
int err;
BT_DBG("0x%03x", net_idx);
sub = subnet_alloc(net_idx);
if (!sub) {
return STATUS_INSUFF_RESOURCES;
}
if (sub->net_idx == net_idx) {
if (memcmp(key, sub->keys[0].net, 16)) {
return STATUS_IDX_ALREADY_STORED;
}
return STATUS_SUCCESS;
}
err = net_keys_create(&sub->keys[0], key);
if (err) {
return STATUS_UNSPECIFIED;
}
sub->net_idx = net_idx;
sub->kr_phase = BT_MESH_KR_NORMAL;
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
} else {
sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
}
subnet_evt(sub, BT_MESH_KEY_ADDED);
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
BT_DBG("Storing NetKey persistently");
bt_mesh_store_subnet(sub->net_idx);
}
return STATUS_SUCCESS;
}
bool bt_mesh_subnet_exists(uint16_t net_idx)
{
return !!bt_mesh_subnet_get(net_idx);
}
uint8_t bt_mesh_subnet_update(uint16_t net_idx, const uint8_t key[16])
{
struct bt_mesh_subnet *sub;
int err;
BT_DBG("0x%03x", net_idx);
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
/* The node shall successfully process a NetKey Update message on a
* valid NetKeyIndex when the NetKey value is different and the Key
* Refresh procedure has not been started, or when the NetKey value is
* the same in Phase 1. The NetKey Update message shall generate an
* error when the node is in Phase 2, or Phase 3.
*/
switch (sub->kr_phase) {
case BT_MESH_KR_NORMAL:
if (!memcmp(key, sub->keys[0].net, 16)) {
return STATUS_IDX_ALREADY_STORED;
}
break;
case BT_MESH_KR_PHASE_1:
if (!memcmp(key, sub->keys[1].net, 16)) {
return STATUS_SUCCESS;
}
__fallthrough;
case BT_MESH_KR_PHASE_2:
case BT_MESH_KR_PHASE_3:
return STATUS_CANNOT_UPDATE;
}
err = net_keys_create(&sub->keys[1], key);
if (err) {
return STATUS_CANNOT_UPDATE;
}
key_refresh(sub, BT_MESH_KR_PHASE_1);
return STATUS_SUCCESS;
}
uint8_t bt_mesh_subnet_del(uint16_t net_idx)
{
struct bt_mesh_subnet *sub;
BT_DBG("0x%03x", net_idx);
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
/* This could be a retry of a previous attempt that had its
* response lost, so pretend that it was a success.
*/
return STATUS_INVALID_NETKEY;
}
subnet_del(sub);
return STATUS_SUCCESS;
}
int bt_mesh_friend_cred_create(struct bt_mesh_net_cred *cred, uint16_t lpn_addr,
uint16_t frnd_addr, uint16_t lpn_counter,
uint16_t frnd_counter, const uint8_t key[16])
{
uint8_t p[9];
p[0] = 0x01;
sys_put_be16(lpn_addr, p + 1);
sys_put_be16(frnd_addr, p + 3);
sys_put_be16(lpn_counter, p + 5);
sys_put_be16(frnd_counter, p + 7);
return msg_cred_create(cred, p, sizeof(p), key);
}
uint8_t bt_mesh_subnet_kr_phase_set(uint16_t net_idx, uint8_t *phase)
{
/* Table in Bluetooth Mesh Profile Specification Section 4.2.14: */
const uint8_t valid_transitions[] = {
0x00, /* Normal phase: KR is started by key update */
BIT(BT_MESH_KR_PHASE_2) | BIT(BT_MESH_KR_PHASE_3), /* Phase 1 */
BIT(BT_MESH_KR_PHASE_3), /* Phase 2 */
/* Subnet is never in Phase 3 */
};
struct bt_mesh_subnet *sub;
BT_DBG("0x%03x", net_idx);
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
*phase = 0x00;
return STATUS_INVALID_NETKEY;
}
if (*phase == sub->kr_phase) {
return STATUS_SUCCESS;
}
if (sub->kr_phase < ARRAY_SIZE(valid_transitions) &&
valid_transitions[sub->kr_phase] & BIT(*phase)) {
key_refresh(sub, *phase);
*phase = sub->kr_phase;
return STATUS_SUCCESS;
}
BT_WARN("Invalid KR transition: 0x%02x -> 0x%02x", sub->kr_phase,
*phase);
*phase = sub->kr_phase;
return STATUS_CANNOT_UPDATE;
}
uint8_t bt_mesh_subnet_kr_phase_get(uint16_t net_idx, uint8_t *phase)
{
struct bt_mesh_subnet *sub;
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
*phase = BT_MESH_KR_NORMAL;
return STATUS_INVALID_NETKEY;
}
*phase = sub->kr_phase;
return STATUS_SUCCESS;
}
uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx,
enum bt_mesh_feat_state node_id)
{
struct bt_mesh_subnet *sub;
if (node_id == BT_MESH_FEATURE_NOT_SUPPORTED) {
return STATUS_CANNOT_SET;
}
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
return STATUS_INVALID_NETKEY;
}
if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
return STATUS_FEAT_NOT_SUPP;
}
if (node_id) {
bt_mesh_proxy_identity_start(sub);
} else {
bt_mesh_proxy_identity_stop(sub);
}
bt_mesh_adv_update();
return STATUS_SUCCESS;
}
uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx,
enum bt_mesh_feat_state *node_id)
{
struct bt_mesh_subnet *sub;
sub = bt_mesh_subnet_get(net_idx);
if (!sub) {
*node_id = 0x00;
return STATUS_INVALID_NETKEY;
}
*node_id = sub->node_id;
return STATUS_SUCCESS;
}
ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip)
{
size_t count = 0;
for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
struct bt_mesh_subnet *sub = &subnets[i];
if (sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (skip) {
skip--;
continue;
}
if (count >= max) {
return -ENOMEM;
}
net_idxs[count++] = sub->net_idx;
}
return count;
}
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx)
{
for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
struct bt_mesh_subnet *sub = &subnets[i];
if (sub->net_idx == net_idx) {
return sub;
}
}
return NULL;
}
int bt_mesh_subnet_set(uint16_t net_idx, uint8_t kr_phase,
const uint8_t old_key[16], const uint8_t new_key[16])
{
const uint8_t *keys[] = { old_key, new_key };
struct bt_mesh_subnet *sub;
sub = subnet_alloc(net_idx);
if (!sub) {
return -ENOMEM;
}
if (sub->net_idx == net_idx) {
return -EALREADY;
}
for (int i = 0; i < ARRAY_SIZE(keys); i++) {
if (!keys[i]) {
continue;
}
if (net_keys_create(&sub->keys[i], keys[i])) {
return -EIO;
}
}
sub->net_idx = net_idx;
sub->kr_phase = kr_phase;
if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY)) {
sub->node_id = BT_MESH_NODE_IDENTITY_STOPPED;
} else {
sub->node_id = BT_MESH_NODE_IDENTITY_NOT_SUPPORTED;
}
/* Make sure we have valid beacon data to be sent */
bt_mesh_beacon_update(sub);
return 0;
}
struct bt_mesh_subnet *bt_mesh_subnet_find(int (*cb)(struct bt_mesh_subnet *sub,
void *cb_data),
void *cb_data)
{
for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
if (subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
if (!cb || cb(&subnets[i], cb_data)) {
return &subnets[i];
}
}
return NULL;
}
size_t bt_mesh_subnet_foreach(void (*cb)(struct bt_mesh_subnet *sub))
{
size_t count = 0;
for (int i = 0; i < ARRAY_SIZE(subnets); i++) {
if (subnets[i].net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
cb(&subnets[i]);
count++;
}
return count;
}
struct bt_mesh_subnet *bt_mesh_subnet_next(struct bt_mesh_subnet *sub)
{
if (sub) {
sub++;
} else {
sub = &subnets[0];
}
for (int i = 0; i < ARRAY_SIZE(subnets); i++, sub++) {
/* Roll over once we reach the end */
if (sub == &subnets[ARRAY_SIZE(subnets)]) {
sub = &subnets[0];
}
if (sub->net_idx != BT_MESH_KEY_UNUSED) {
return sub;
}
}
return NULL;
}
void bt_mesh_net_keys_reset(void)
{
int i;
/* Delete all net keys, which also takes care of all app keys which
* are associated with each net key.
*/
for (i = 0; i < ARRAY_SIZE(subnets); i++) {
struct bt_mesh_subnet *sub = &subnets[i];
if (sub->net_idx != BT_MESH_KEY_UNUSED) {
subnet_del(sub);
}
}
}
bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct net_buf_simple *in,
struct net_buf_simple *out,
bool (*cb)(struct bt_mesh_net_rx *rx,
struct net_buf_simple *in,
struct net_buf_simple *out,
const struct bt_mesh_net_cred *cred))
{
int i, j;
BT_DBG("");
rx->friend_cred = 1U;
#if defined(CONFIG_BT_MESH_LOW_POWER)
if (bt_mesh_lpn_established()) {
rx->sub = bt_mesh.lpn.sub;
for (j = 0; j < ARRAY_SIZE(bt_mesh.lpn.cred); j++) {
if (!rx->sub->keys[j].valid) {
continue;
}
if (cb(rx, in, out, &bt_mesh.lpn.cred[j])) {
rx->new_key = (j > 0);
return true;
}
}
/* LPN Should only receive on the friendship credentials when in
* a friendship.
*/
return false;
}
#endif
#if defined(CONFIG_BT_MESH_FRIEND)
/** Each friendship has unique friendship credentials */
for (i = 0; i < ARRAY_SIZE(bt_mesh.frnd); i++) {
struct bt_mesh_friend *frnd = &bt_mesh.frnd[i];
if (!frnd->established) {
continue;
}
if (!frnd->subnet) {
continue;
}
rx->sub = frnd->subnet;
for (j = 0; j < ARRAY_SIZE(frnd->cred); j++) {
if (!rx->sub->keys[j].valid) {
continue;
}
if (cb(rx, in, out, &frnd->cred[j])) {
rx->new_key = (j > 0);
return true;
}
}
}
#endif
for (i = 0; i < ARRAY_SIZE(subnets); i++) {
rx->sub = &subnets[i];
if (rx->sub->net_idx == BT_MESH_KEY_UNUSED) {
continue;
}
for (j = 0; j < ARRAY_SIZE(rx->sub->keys); j++) {
if (!rx->sub->keys[j].valid) {
continue;
}
if (cb(rx, in, out, &rx->sub->keys[j].msg)) {
rx->new_key = (j > 0);
return true;
}
}
}
return false;
}

View file

@ -0,0 +1,203 @@
/*
* Copyright (c) 2020 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_
#define ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_
#include <stdint.h>
#include <sys/types.h>
#include <net/buf.h>
#include <zephyr.h>
#define BT_MESH_NET_FLAG_KR BIT(0)
#define BT_MESH_NET_FLAG_IVU BIT(1)
#define BT_MESH_KR_NORMAL 0x00
#define BT_MESH_KR_PHASE_1 0x01
#define BT_MESH_KR_PHASE_2 0x02
#define BT_MESH_KR_PHASE_3 0x03
/** Which of the two subnet.keys should be used for sending. */
#define SUBNET_KEY_TX_IDX(sub) ((sub)->kr_phase == BT_MESH_KR_PHASE_2)
struct bt_mesh_net_rx;
enum bt_mesh_key_evt;
/** Network message encryption credentials */
struct bt_mesh_net_cred {
uint8_t nid; /* NID */
uint8_t enc[16]; /* EncKey */
uint8_t privacy[16]; /* PrivacyKey */
};
/** Subnet instance. */
struct bt_mesh_subnet {
uint32_t beacon_sent; /* Timestamp of last sent beacon */
uint8_t beacons_last; /* Number of beacons during last
* observation window
*/
uint8_t beacons_cur; /* Number of beaconds observed during
* currently ongoing window.
*/
uint8_t beacon_cache[21]; /* Cached last authenticated beacon */
uint16_t net_idx; /* NetKeyIndex */
uint8_t kr_phase; /* Key Refresh Phase */
uint8_t node_id; /* Node Identity State */
uint32_t node_id_start; /* Node Identity started timestamp */
uint8_t auth[8]; /* Beacon Authentication Value */
struct bt_mesh_subnet_keys {
bool valid;
uint8_t net[16]; /* NetKey */
struct bt_mesh_net_cred msg;
uint8_t net_id[8]; /* Network ID */
#if defined(CONFIG_BT_MESH_GATT_PROXY)
uint8_t identity[16]; /* IdentityKey */
#endif
uint8_t beacon[16]; /* BeaconKey */
} keys[2];
};
/** Subnet callback structure. Instantiate with @ref BT_MESH_SUBNET_CB */
struct bt_mesh_subnet_cb {
void (*evt_handler)(struct bt_mesh_subnet *subnet,
enum bt_mesh_key_evt evt);
};
/** @def BT_MESH_SUBNET_CB
*
* @brief Register a subnet event callback.
*
* @param _handler Handler function, see @ref bt_mesh_subnet_cb::evt_handler.
*/
#define BT_MESH_SUBNET_CB_DEFINE(_handler) \
static const Z_STRUCT_SECTION_ITERABLE( \
bt_mesh_subnet_cb, _CONCAT(bt_mesh_subnet_cb_, _handler)) = { \
.evt_handler = (_handler), \
}
/** @brief Reset all Network keys. */
void bt_mesh_net_keys_reset(void);
/** @brief Call cb on every valid Subnet until it returns a non-zero value.
*
* @param cb Callback to call, or NULL to return first valid subnet.
* @param cb_data Callback data to pass to callback.
*
* @return Subnet that returned non-zero value.
*/
struct bt_mesh_subnet *bt_mesh_subnet_find(int (*cb)(struct bt_mesh_subnet *sub,
void *cb_data),
void *cb_data);
/** @brief Iterate through all valid Subnets.
*
* @param cb Callback to call on every Subnet.
*
* @returns The number of valid subnets.
*/
size_t bt_mesh_subnet_foreach(void (*cb)(struct bt_mesh_subnet *sub));
/** @brief Get the next valid Subnet.
*
* If there's only one valid Subnet, this will be returned on every call.
*
* @param sub Previous Subnet, or NULL to get the first valid.
*
* @returns Gets the next valid Subnet after @c sub, or NULL if there are no
* valid Subnets.
*/
struct bt_mesh_subnet *bt_mesh_subnet_next(struct bt_mesh_subnet *sub);
/** @brief Get a pointer to the Subnet with the given index.
*
* @param net_idx Network index to look for.
*
* @returns Subnet with index @c net_idx, or NULL if no such Subnet is known.
*/
struct bt_mesh_subnet *bt_mesh_subnet_get(uint16_t net_idx);
/** @brief Initialize a new Subnet.
*
* @param net_idx Network index of the Subnet.
* @param kr_phase Key refresh phase the Subnet should be in.
* @param key The current network key for the Subnet.
* @param new_key New network key, if available.
*
* @returns 0 on success, or (negative) error code on failure.
*/
int bt_mesh_subnet_set(uint16_t net_idx, uint8_t kr_phase,
const uint8_t key[16], const uint8_t new_key[16]);
/** @brief Create Friendship credentials.
*
* @param cred Credential object to create.
* @param lpn_addr Address of the LPN node in the friendship.
* @param frnd_addr Address of the Friend node in the friendship.
* @param lpn_counter The LPN's counter parameter.
* @param frnd_counter The Friend node's counter parameter.
* @param key Network key to create the Friendship credentials for.
*
* @returns 0 on success, or (negative) error code on failure.
*/
int bt_mesh_friend_cred_create(struct bt_mesh_net_cred *cred,
uint16_t lpn_addr, uint16_t frnd_addr,
uint16_t lpn_counter, uint16_t frnd_counter,
const uint8_t key[16]);
/** @brief Iterate through all valid network credentials to decrypt a message.
*
* @param rx Network RX parameters, passed to the callback.
* @param in Input message buffer, passed to the callback.
* @param out Output message buffer, passed to the callback.
* @param cb Callback to call for each known network credential. Iteration
* stops when this callback returns @c true.
*
* @returns Whether any of the credentials got a @c true return from the
* callback.
*/
bool bt_mesh_net_cred_find(struct bt_mesh_net_rx *rx, struct net_buf_simple *in,
struct net_buf_simple *out,
bool (*cb)(struct bt_mesh_net_rx *rx,
struct net_buf_simple *in,
struct net_buf_simple *out,
const struct bt_mesh_net_cred *cred));
/** @brief Get the network flags of the given Subnet.
*
* @param sub Subnet to get the network flags of.
*
* @returns A bitmap of @ref BT_MESH_NET_FLAG_KR and @ref BT_MESH_NET_FLAG_IVU.
*/
uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub);
/** @brief Process a Key Refresh event from a beacon.
*
* @param sub Subnet the Key Refresh was received on.
* @param kr_flag Key Refresh flag.
* @param new_key Whether the Key Refresh event was received on the new key
* set.
*/
void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key);
/** @brief Check whether the Subnet has the refreshed keys.
*
* @param sub Subnet.
*
* @returns Whether the Subnet's second key is valid.
*/
static inline bool
bt_mesh_subnet_has_new_key(const struct bt_mesh_subnet *sub)
{
return sub->kr_phase != BT_MESH_KR_NORMAL;
}
#endif /* ZEPHYR_SUBSYS_BLUETOOTH_MESH_SUBNET_H_ */

View file

@ -28,6 +28,7 @@
#include "adv.h"
#include "mesh.h"
#include "net.h"
#include "app_keys.h"
#include "lpn.h"
#include "rpl.h"
#include "friend.h"
@ -268,9 +269,7 @@ static void seg_tx_reset(struct seg_tx *tx)
/* bt_mesh_net_iv_update() will re-enable the flag if this
* wasn't the only transfer.
*/
if (bt_mesh_net_iv_update(bt_mesh.iv_index, false)) {
bt_mesh_net_sec_update(NULL);
}
bt_mesh_net_iv_update(bt_mesh.iv_index, false);
}
}
@ -596,27 +595,18 @@ static int send_seg(struct bt_mesh_net_tx *net_tx, struct net_buf_simple *sdu,
return 0;
}
struct bt_mesh_app_key *bt_mesh_app_key_find(uint16_t app_idx)
{
int i;
for (i = 0; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
if (key->net_idx != BT_MESH_KEY_UNUSED &&
key->app_idx == app_idx) {
return key;
}
}
return NULL;
}
int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
const struct bt_mesh_send_cb *cb, void *cb_data)
{
struct bt_mesh_app_crypto_ctx crypto = {
.dev_key = BT_MESH_IS_DEV_KEY(tx->ctx->app_idx),
.aszmic = tx->aszmic,
.src = tx->src,
.dst = tx->ctx->addr,
.seq_num = bt_mesh.seq,
.iv_index = BT_MESH_NET_IVI_TX,
};
const uint8_t *key;
uint8_t *ad;
uint8_t aid;
int err;
@ -643,12 +633,12 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
tx->ctx->app_idx, tx->ctx->addr);
BT_DBG("len %u: %s", msg->len, bt_hex(msg->data, msg->len));
err = bt_mesh_app_key_get(tx->sub, tx->ctx->app_idx,
tx->ctx->addr, &key, &aid);
err = bt_mesh_keys_resolve(tx->ctx, &tx->sub, &key, &aid);
if (err) {
return err;
}
tx->xmit = bt_mesh_net_transmit_get();
tx->aid = aid;
if (!tx->ctx->send_rel || net_buf_simple_tailroom(msg) < 8) {
@ -658,14 +648,10 @@ int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
}
if (BT_MESH_ADDR_IS_VIRTUAL(tx->ctx->addr)) {
ad = bt_mesh_label_uuid_get(tx->ctx->addr);
} else {
ad = NULL;
crypto.ad = bt_mesh_label_uuid_get(tx->ctx->addr);
}
err = bt_mesh_app_encrypt(key, BT_MESH_IS_DEV_KEY(tx->ctx->app_idx),
tx->aszmic, msg, ad, tx->src, tx->ctx->addr,
bt_mesh.seq, BT_MESH_NET_IVI_TX);
err = bt_mesh_app_encrypt(key, &crypto, msg);
if (err) {
return err;
}
@ -698,93 +684,46 @@ static void seg_rx_assemble(struct seg_rx *rx, struct net_buf_simple *buf,
}
}
static int remote_devkey_decrypt(struct bt_mesh_net_rx *rx, uint32_t seq, uint8_t *ad,
uint8_t aszmic, struct net_buf_simple *buf,
struct net_buf_simple *sdu)
struct decrypt_ctx {
struct bt_mesh_app_crypto_ctx crypto;
struct net_buf_simple *buf;
struct net_buf_simple *sdu;
struct seg_rx *seg;
};
static int sdu_try_decrypt(struct bt_mesh_net_rx *rx, const uint8_t key[16],
void *cb_data)
{
struct bt_mesh_cdb_node *node;
int err;
const struct decrypt_ctx *ctx = cb_data;
if (!IS_ENABLED(CONFIG_BT_MESH_PROVISIONER)) {
return -ENOENT;
if (ctx->seg) {
seg_rx_assemble(ctx->seg, ctx->buf, ctx->crypto.aszmic);
}
/* We will try our local devkey separately. */
if (bt_mesh_elem_find(rx->ctx.addr)) {
return -ENOENT;
}
net_buf_simple_reset(ctx->sdu);
/*
* There is no way of knowing if we should use our
* local DevKey or the remote DevKey to decrypt the
* message so we must try both.
*/
node = bt_mesh_cdb_node_get(rx->ctx.addr);
if (node == NULL) {
BT_ERR("No node found for addr 0x%04x", rx->ctx.addr);
return -EINVAL;
}
err = bt_mesh_app_decrypt(node->dev_key, true, aszmic, buf, sdu, ad,
rx->ctx.addr, rx->ctx.recv_dst, seq,
BT_MESH_NET_IVI_RX(rx));
if (err) {
BT_DBG("Unable to decrypt with node DevKey");
return -EINVAL;
}
return 0;
return bt_mesh_app_decrypt(key, &ctx->crypto, ctx->buf, ctx->sdu);
}
static int app_key_decrypt(struct bt_mesh_net_rx *rx,
struct bt_mesh_app_key *key, uint32_t seq, uint8_t *ad,
uint8_t hdr, uint8_t aszmic, struct net_buf_simple *buf,
struct net_buf_simple *sdu)
static int sdu_recv(struct bt_mesh_net_rx *rx, uint8_t hdr, uint8_t aszmic,
struct net_buf_simple *buf, struct net_buf_simple *sdu,
struct seg_rx *seg)
{
struct bt_mesh_app_keys *keys;
int err;
struct decrypt_ctx ctx = {
.crypto = {
.dev_key = !AKF(&hdr),
.aszmic = aszmic,
.src = rx->ctx.addr,
.dst = rx->ctx.recv_dst,
.seq_num = rx->seq,
.iv_index = BT_MESH_NET_IVI_RX(rx),
},
.buf = buf,
.sdu = sdu,
.seg = seg,
};
/* Check that this AppKey matches received net_idx */
if (key->net_idx != rx->sub->net_idx) {
return -EINVAL;
}
if (rx->new_key && key->updated) {
keys = &key->keys[1];
} else {
keys = &key->keys[0];
}
/* Check that the AppKey ID matches */
if (AID(&hdr) != keys->id) {
return -EINVAL;
}
err = bt_mesh_app_decrypt(keys->val, false, aszmic, buf, sdu, ad,
rx->ctx.addr, rx->ctx.recv_dst, seq,
BT_MESH_NET_IVI_RX(rx));
if (err) {
BT_WARN("Unable to decrypt with AppKey 0x%03x", key->app_idx);
}
return err;
}
static int sdu_recv_unseg(struct bt_mesh_net_rx *rx, uint8_t hdr,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_SDU_UNSEG_MAX);
uint8_t *ad;
uint16_t i;
int err;
BT_DBG("AKF %u AID 0x%02x", AKF(&hdr), AID(&hdr));
BT_DBG("len %u: %s", buf->len, bt_hex(buf->data, buf->len));
if (buf->len < 1 + APP_MIC_LEN(0)) {
BT_ERR("Too short SDU + MIC");
return -EINVAL;
}
BT_DBG("AKF %u AID 0x%02x", !ctx.crypto.dev_key, AID(&hdr));
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) {
BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend",
@ -793,157 +732,19 @@ static int sdu_recv_unseg(struct bt_mesh_net_rx *rx, uint8_t hdr,
}
if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) {
ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
} else {
ad = NULL;
ctx.crypto.ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
}
/* Adjust the length to not contain the MIC at the end */
buf->len -= APP_MIC_LEN(0);
if (!AKF(&hdr)) {
/* 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.
*/
net_buf_simple_reset(&sdu);
err = remote_devkey_decrypt(rx, rx->seq, ad, 0, buf, &sdu);
if (!err) {
rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE;
bt_mesh_model_recv(rx, &sdu);
return 0;
}
net_buf_simple_reset(&sdu);
err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, 0, buf, &sdu,
ad, rx->ctx.addr, rx->ctx.recv_dst,
rx->seq, BT_MESH_NET_IVI_RX(rx));
if (err) {
BT_ERR("Unable to decrypt with local DevKey");
return err;
}
rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL;
bt_mesh_model_recv(rx, &sdu);
rx->ctx.app_idx = bt_mesh_app_key_find(ctx.crypto.dev_key, AID(&hdr),
rx, sdu_try_decrypt, &ctx);
if (rx->ctx.app_idx == BT_MESH_KEY_UNUSED) {
BT_DBG("No matching AppKey");
return 0;
}
for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
BT_DBG("Decrypted (AppIdx: 0x%03x)", rx->ctx.app_idx);
net_buf_simple_reset(&sdu);
err = app_key_decrypt(rx, &bt_mesh.app_keys[i], rx->seq, ad,
hdr, 0, buf, &sdu);
if (err) {
continue;
}
rx->ctx.app_idx = key->app_idx;
bt_mesh_model_recv(rx, &sdu);
return 0;
}
if (rx->local_match) {
BT_WARN("No matching AppKey");
}
return 0;
}
static int sdu_recv_seg(struct seg_rx *seg, uint8_t hdr, uint8_t aszmic,
struct bt_mesh_net_rx *rx)
{
NET_BUF_SIMPLE_DEFINE(buf, BT_MESH_RX_SDU_MAX);
struct net_buf_simple sdu;
uint32_t seq = (seg->seq_auth & 0xffffff);
uint8_t *ad;
uint16_t i;
int err;
BT_DBG("ASZMIC %u AKF %u AID 0x%02x", aszmic, AKF(&hdr), AID(&hdr));
if (seg->len < 1 + APP_MIC_LEN(aszmic)) {
BT_ERR("Too short SDU + MIC");
return -EINVAL;
}
if (IS_ENABLED(CONFIG_BT_MESH_FRIEND) && !rx->local_match) {
BT_DBG("Ignoring PDU for LPN 0x%04x of this Friend",
rx->ctx.recv_dst);
return 0;
}
if (BT_MESH_ADDR_IS_VIRTUAL(rx->ctx.recv_dst)) {
ad = bt_mesh_label_uuid_get(rx->ctx.recv_dst);
} else {
ad = NULL;
}
/* Decrypting in place to avoid creating two assembly buffers.
* We'll reassemble the buffer from the segments before each decryption
* attempt.
*/
if (!AKF(&hdr)) {
/* 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.
*/
seg_rx_assemble(seg, &buf, aszmic);
net_buf_simple_init_with_data(&sdu, buf.data,
seg->len - APP_MIC_LEN(aszmic));
sdu.len = 0;
err = remote_devkey_decrypt(rx, seq, ad, aszmic, &buf, &sdu);
if (!err) {
rx->ctx.app_idx = BT_MESH_KEY_DEV_REMOTE;
bt_mesh_model_recv(rx, &sdu);
return 0;
}
seg_rx_assemble(seg, &buf, aszmic);
net_buf_simple_init_with_data(&sdu, buf.data,
seg->len - APP_MIC_LEN(aszmic));
sdu.len = 0;
err = bt_mesh_app_decrypt(bt_mesh.dev_key, true, aszmic, &buf,
&sdu, ad, rx->ctx.addr,
rx->ctx.recv_dst, seq,
BT_MESH_NET_IVI_RX(rx));
if (err) {
BT_ERR("Unable to decrypt with local DevKey");
return err;
}
rx->ctx.app_idx = BT_MESH_KEY_DEV_LOCAL;
bt_mesh_model_recv(rx, &sdu);
return 0;
}
for (i = 0U; i < ARRAY_SIZE(bt_mesh.app_keys); i++) {
struct bt_mesh_app_key *key = &bt_mesh.app_keys[i];
seg_rx_assemble(seg, &buf, aszmic);
net_buf_simple_init_with_data(&sdu, buf.data,
seg->len - APP_MIC_LEN(aszmic));
sdu.len = 0;
err = app_key_decrypt(rx, &bt_mesh.app_keys[i], seq, ad, hdr,
aszmic, &buf, &sdu);
if (err) {
continue;
}
rx->ctx.app_idx = key->app_idx;
bt_mesh_model_recv(rx, &sdu);
return 0;
}
if (rx->local_match) {
BT_WARN("No matching AppKey");
}
bt_mesh_model_recv(rx, sdu);
return 0;
}
@ -1150,6 +951,7 @@ static int ctl_recv(struct bt_mesh_net_rx *rx, uint8_t hdr,
static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
uint64_t *seq_auth)
{
NET_BUF_SIMPLE_DEFINE_STATIC(sdu, BT_MESH_SDU_UNSEG_MAX);
uint8_t hdr;
BT_DBG("AFK %u AID 0x%02x", AKF(buf->data), AID(buf->data));
@ -1169,14 +971,17 @@ static int trans_unseg(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx,
if (rx->ctl) {
return ctl_recv(rx, hdr, buf, seq_auth);
} else {
/* SDUs must match a local element or an LPN of this Friend. */
if (!rx->local_match && !rx->friend_match) {
return 0;
}
return sdu_recv_unseg(rx, hdr, buf);
}
if (buf->len < 1 + APP_MIC_LEN(0)) {
BT_ERR("Too short SDU + MIC");
return -EINVAL;
}
/* Adjust the length to not contain the MIC at the end */
buf->len -= APP_MIC_LEN(0);
return sdu_recv(rx, hdr, 0, buf, &sdu, NULL);
}
static inline int32_t ack_timeout(struct seg_rx *rx)
@ -1643,12 +1448,29 @@ found_rx:
send_ack(net_rx->sub, net_rx->ctx.recv_dst, net_rx->ctx.addr,
net_rx->ctx.send_ttl, seq_auth, rx->block, rx->obo);
/* Decrypt with seqAuth */
net_rx->seq = (rx->seq_auth & 0xffffff);
if (net_rx->ctl) {
NET_BUF_SIMPLE_DEFINE(sdu, BT_MESH_RX_CTL_MAX);
seg_rx_assemble(rx, &sdu, 0U);
err = ctl_recv(net_rx, *hdr, &sdu, seq_auth);
} else if (rx->len < 1 + APP_MIC_LEN(ASZMIC(hdr))) {
BT_ERR("Too short SDU + MIC");
err = -EINVAL;
} else {
err = sdu_recv_seg(rx, *hdr, ASZMIC(hdr), net_rx);
NET_BUF_SIMPLE_DEFINE_STATIC(seg_buf, BT_MESH_RX_SDU_MAX);
struct net_buf_simple sdu;
/* Decrypting in place to avoid creating two assembly buffers.
* We'll reassemble the buffer from the segments before each
* decryption attempt.
*/
net_buf_simple_init(&seg_buf, 0);
net_buf_simple_init_with_data(
&sdu, seg_buf.data, rx->len - APP_MIC_LEN(ASZMIC(hdr)));
err = sdu_recv(net_rx, *hdr, ASZMIC(hdr), &seg_buf, &sdu, rx);
}
seg_rx_reset(rx, false);
@ -1834,49 +1656,3 @@ int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data)
return bt_mesh_ctl_send(&tx, TRANS_CTL_OP_HEARTBEAT, &hb, sizeof(hb),
cb, cb_data);
}
int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, uint16_t app_idx,
uint16_t addr, const uint8_t **key, uint8_t *aid)
{
struct bt_mesh_app_key *app_key;
if (app_idx == BT_MESH_KEY_DEV_LOCAL ||
(app_idx == BT_MESH_KEY_DEV_REMOTE &&
bt_mesh_elem_find(addr) != NULL)) {
*aid = 0;
*key = bt_mesh.dev_key;
return 0;
} else if (app_idx == BT_MESH_KEY_DEV_REMOTE) {
if (!IS_ENABLED(CONFIG_BT_MESH_CDB)) {
return -EINVAL;
}
struct bt_mesh_cdb_node *node = bt_mesh_cdb_node_get(addr);
if (!node) {
return -EINVAL;
}
*key = node->dev_key;
*aid = 0;
return 0;
}
if (!subnet) {
return -EINVAL;
}
app_key = bt_mesh_app_key_find(app_idx);
if (!app_key) {
return -ENOENT;
}
if (subnet->kr_phase == BT_MESH_KR_PHASE_2 && app_key->updated) {
*key = app_key->keys[1].val;
*aid = app_key->keys[1].id;
} else {
*key = app_key->keys[0].val;
*aid = app_key->keys[0].id;
}
return 0;
}

View file

@ -81,8 +81,6 @@ struct bt_mesh_ctl_friend_sub_confirm {
void bt_mesh_set_hb_sub_dst(uint16_t addr);
struct bt_mesh_app_key *bt_mesh_app_key_find(uint16_t app_idx);
bool bt_mesh_tx_in_progress(void);
void bt_mesh_rx_reset(void);
@ -92,6 +90,16 @@ int bt_mesh_ctl_send(struct bt_mesh_net_tx *tx, uint8_t ctl_op, void *data,
size_t data_len, const struct bt_mesh_send_cb *cb,
void *cb_data);
/** @brief Send an access payload message.
*
* @param tx Network TX parameters. Only @c ctx, @c src and @c friend_cred
* have to be filled.
* @param msg Access payload to send.
* @param cb Message callback.
* @param cb_data Message callback data.
*
* @return 0 on success, or (negative) error code otherwise.
*/
int bt_mesh_trans_send(struct bt_mesh_net_tx *tx, struct net_buf_simple *msg,
const struct bt_mesh_send_cb *cb, void *cb_data);
@ -100,6 +108,3 @@ int bt_mesh_trans_recv(struct net_buf_simple *buf, struct bt_mesh_net_rx *rx);
void bt_mesh_trans_init(void);
int bt_mesh_heartbeat_send(const struct bt_mesh_send_cb *cb, void *cb_data);
int bt_mesh_app_key_get(const struct bt_mesh_subnet *subnet, uint16_t app_idx,
uint16_t addr, const uint8_t **key, uint8_t *aid);