A mesh key type has been added to be able to choose the different key representation for different security libraries. The type as well as some functionality related to Mesh key management has been added as a public API. If tynicrypt is chosen then keys have representation as 16 bytes array. If mbedTLS with PSA is used then keys are the PSA key id. Raw value is not kept within BLE Mesh stack for mbedTLS. Keys are imported into the security library and key ids are gotten back. This refactoring has been done for the network(including all derivated keys), application, device, and session keys. Signed-off-by: Aleksandr Khromykh <aleksandr.khromykh@nordicsemi.no>
801 lines
19 KiB
C
801 lines
19 KiB
C
/*
|
|
* Copyright (c) 2017 Intel Corporation
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <errno.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/sys/byteorder.h>
|
|
#include <zephyr/sys/iterable_sections.h>
|
|
#include <zephyr/net/buf.h>
|
|
#include <zephyr/bluetooth/bluetooth.h>
|
|
#include <zephyr/bluetooth/conn.h>
|
|
#include <zephyr/bluetooth/mesh.h>
|
|
|
|
#include "common/bt_str.h"
|
|
|
|
#include "adv.h"
|
|
#include "mesh.h"
|
|
#include "net.h"
|
|
#include "prov.h"
|
|
#include "crypto.h"
|
|
#include "beacon.h"
|
|
#include "cfg.h"
|
|
|
|
#define LOG_LEVEL CONFIG_BT_MESH_BEACON_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(bt_mesh_beacon);
|
|
|
|
#define PROVISIONED_INTERVAL K_SECONDS(10)
|
|
|
|
#define BEACON_TYPE_UNPROVISIONED 0x00
|
|
#define BEACON_TYPE_SECURE 0x01
|
|
#define BEACON_TYPE_PRIVATE 0x02
|
|
|
|
/* 3 transmissions, 20ms interval */
|
|
#define UNPROV_XMIT BT_MESH_TRANSMIT(2, 20)
|
|
|
|
/* 1 transmission, 20ms interval */
|
|
#define PROV_XMIT BT_MESH_TRANSMIT(0, 20)
|
|
|
|
static struct k_work_delayable beacon_timer;
|
|
static struct {
|
|
/**
|
|
* Identifier for the current Private beacon random-value.
|
|
* Each time we regenerate the random-value, we'll update this idx.
|
|
* Whenever it's time for a subnet to create a beacon, it'll compare
|
|
* the subnet's beacon idx to determine whether the random value has
|
|
* changed since the last beacon was sent. If this is the case, we'll
|
|
* regenerate the beacon based on the new random value.
|
|
*/
|
|
uint16_t idx;
|
|
uint8_t val[13];
|
|
uint64_t timestamp;
|
|
} priv_random;
|
|
|
|
struct beacon_params {
|
|
bool private;
|
|
union {
|
|
const uint8_t *net_id;
|
|
struct {
|
|
const uint8_t *data;
|
|
const uint8_t *random;
|
|
};
|
|
};
|
|
const uint8_t *auth;
|
|
uint32_t iv_index;
|
|
uint8_t flags;
|
|
|
|
bool new_key;
|
|
};
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
static int private_beacon_create(struct bt_mesh_subnet *sub,
|
|
struct net_buf_simple *buf);
|
|
static int private_beacon_update(struct bt_mesh_subnet *sub);
|
|
#endif
|
|
|
|
static struct bt_mesh_beacon *subnet_beacon_get_by_type(struct bt_mesh_subnet *sub, bool priv)
|
|
{
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
return priv ? &sub->priv_beacon : &sub->secure_beacon;
|
|
#else
|
|
return &sub->secure_beacon;
|
|
#endif
|
|
}
|
|
|
|
static bool beacon_cache_match(struct bt_mesh_subnet *sub, void *data)
|
|
{
|
|
struct beacon_params *params;
|
|
struct bt_mesh_beacon *beacon;
|
|
|
|
params = data;
|
|
beacon = subnet_beacon_get_by_type(sub, params->private);
|
|
|
|
return !memcmp(beacon->cache, params->auth, sizeof(beacon->cache));
|
|
}
|
|
|
|
static void cache_add(const uint8_t auth[8], struct bt_mesh_beacon *beacon)
|
|
{
|
|
memcpy(beacon->cache, auth, sizeof(beacon->cache));
|
|
}
|
|
|
|
void bt_mesh_beacon_cache_clear(struct bt_mesh_subnet *sub)
|
|
{
|
|
(void)memset(sub->secure_beacon.cache, 0, sizeof(sub->secure_beacon.cache));
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
(void)memset(sub->priv_beacon.cache, 0, sizeof(sub->priv_beacon.cache));
|
|
#endif
|
|
}
|
|
|
|
static void beacon_complete(int err, void *user_data)
|
|
{
|
|
struct bt_mesh_beacon *beacon = user_data;
|
|
|
|
LOG_DBG("err %d", err);
|
|
|
|
beacon->sent = k_uptime_get_32();
|
|
}
|
|
|
|
static int secure_beacon_create(struct bt_mesh_subnet *sub,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
uint8_t flags = bt_mesh_net_flags(sub);
|
|
struct bt_mesh_subnet_keys *keys;
|
|
|
|
net_buf_simple_add_u8(buf, BEACON_TYPE_SECURE);
|
|
|
|
keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
|
|
|
|
net_buf_simple_add_u8(buf, flags);
|
|
|
|
/* Network ID */
|
|
net_buf_simple_add_mem(buf, keys->net_id, 8);
|
|
|
|
/* IV Index */
|
|
net_buf_simple_add_be32(buf, bt_mesh.iv_index);
|
|
|
|
net_buf_simple_add_mem(buf, sub->secure_beacon.auth, 8);
|
|
|
|
LOG_DBG("net_idx 0x%04x flags 0x%02x NetID %s", sub->net_idx, flags,
|
|
bt_hex(keys->net_id, 8));
|
|
LOG_DBG("IV Index 0x%08x Auth %s", bt_mesh.iv_index, bt_hex(sub->secure_beacon.auth, 8));
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
static int private_random_update(void)
|
|
{
|
|
uint8_t interval = bt_mesh_priv_beacon_update_interval_get();
|
|
uint64_t uptime = k_uptime_get();
|
|
int err;
|
|
|
|
/* The Private beacon random value should change every N seconds to maintain privacy.
|
|
* N = (10 * interval) seconds, or on every beacon creation, if the interval is 0.
|
|
*/
|
|
if (bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED &&
|
|
interval &&
|
|
uptime - priv_random.timestamp < (10 * interval * MSEC_PER_SEC) &&
|
|
priv_random.timestamp != 0) {
|
|
/* Not time yet */
|
|
return 0;
|
|
}
|
|
|
|
err = bt_rand(priv_random.val, sizeof(priv_random.val));
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
/* Update the index to indicate to all subnets that the private beacon must be regenerated.
|
|
* Each subnet maintains the random index their private beacon data was generated with.
|
|
*/
|
|
priv_random.idx++;
|
|
priv_random.timestamp = uptime;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int private_beacon_update(struct bt_mesh_subnet *sub)
|
|
{
|
|
struct bt_mesh_subnet_keys *keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
|
|
uint8_t flags = bt_mesh_net_flags(sub);
|
|
int err;
|
|
|
|
err = bt_mesh_beacon_encrypt(&keys->priv_beacon, flags, bt_mesh.iv_index,
|
|
priv_random.val, sub->priv_beacon_ctx.data,
|
|
sub->priv_beacon.auth);
|
|
if (err) {
|
|
LOG_ERR("Can't encrypt private beacon");
|
|
return err;
|
|
}
|
|
|
|
sub->priv_beacon_ctx.idx = priv_random.idx;
|
|
return 0;
|
|
}
|
|
|
|
static int private_beacon_create(struct bt_mesh_subnet *sub,
|
|
struct net_buf_simple *buf)
|
|
{
|
|
int err;
|
|
|
|
/* Refresh beacon data */
|
|
err = private_random_update();
|
|
if (err) {
|
|
return err;
|
|
}
|
|
|
|
if (sub->priv_beacon_ctx.idx != priv_random.idx) {
|
|
err = private_beacon_update(sub);
|
|
if (err) {
|
|
return err;
|
|
}
|
|
}
|
|
|
|
net_buf_simple_add_u8(buf, BEACON_TYPE_PRIVATE);
|
|
net_buf_simple_add_mem(buf, priv_random.val, 13);
|
|
net_buf_simple_add_mem(buf, sub->priv_beacon_ctx.data, 5);
|
|
net_buf_simple_add_mem(buf, sub->priv_beacon.auth, 8);
|
|
|
|
LOG_DBG("0x%03x", sub->net_idx);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int bt_mesh_beacon_create(struct bt_mesh_subnet *sub, struct net_buf_simple *buf, bool priv)
|
|
{
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
if (priv) {
|
|
return private_beacon_create(sub, buf);
|
|
}
|
|
#endif
|
|
|
|
secure_beacon_create(sub, buf);
|
|
return 0;
|
|
}
|
|
|
|
/* If the interval has passed or is within 5 seconds from now send a beacon */
|
|
#define BEACON_THRESHOLD(beacon) \
|
|
((10 * ((beacon)->last + 1)) * MSEC_PER_SEC - (5 * MSEC_PER_SEC))
|
|
|
|
static bool secure_beacon_is_running(void)
|
|
{
|
|
return bt_mesh_beacon_enabled() ||
|
|
atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR);
|
|
}
|
|
|
|
static bool net_beacon_send(struct bt_mesh_subnet *sub, struct bt_mesh_beacon *beacon,
|
|
void *cb_data, int (*beacon_create)(struct bt_mesh_subnet *sub,
|
|
struct net_buf_simple *buf))
|
|
{
|
|
static const struct bt_mesh_send_cb send_cb = {
|
|
.end = beacon_complete,
|
|
};
|
|
uint32_t now = k_uptime_get_32();
|
|
struct net_buf *buf;
|
|
uint32_t time_diff;
|
|
uint32_t time_since_last_recv;
|
|
int err;
|
|
|
|
LOG_DBG("");
|
|
|
|
time_diff = now - beacon->sent;
|
|
time_since_last_recv = now - beacon->recv;
|
|
if (time_diff < (600 * MSEC_PER_SEC) &&
|
|
(time_diff < BEACON_THRESHOLD(beacon) ||
|
|
time_since_last_recv < (10 * MSEC_PER_SEC))) {
|
|
return false;
|
|
}
|
|
|
|
buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_LOCAL_ADV,
|
|
PROV_XMIT, K_NO_WAIT);
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate beacon buffer");
|
|
return true; /* Bail out */
|
|
}
|
|
|
|
err = beacon_create(sub, &buf->b);
|
|
if (!err) {
|
|
bt_mesh_adv_send(buf, &send_cb, beacon);
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
|
|
return err != 0;
|
|
}
|
|
|
|
static bool net_beacon_for_subnet_send(struct bt_mesh_subnet *sub, void *cb_data)
|
|
{
|
|
bool res = true;
|
|
|
|
struct {
|
|
struct bt_mesh_beacon *beacon;
|
|
bool enabled;
|
|
int (*create_fn)(struct bt_mesh_subnet *sub, struct net_buf_simple *buf);
|
|
} beacons[] = {
|
|
[0] = {
|
|
.beacon = &sub->secure_beacon,
|
|
.enabled = secure_beacon_is_running(),
|
|
.create_fn = secure_beacon_create,
|
|
},
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
[1] = {
|
|
.beacon = &sub->priv_beacon,
|
|
.enabled = bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED,
|
|
.create_fn = private_beacon_create,
|
|
},
|
|
#endif
|
|
};
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(beacons); i++) {
|
|
if (!beacons[i].enabled) {
|
|
continue;
|
|
}
|
|
|
|
res = net_beacon_send(sub, beacons[i].beacon, cb_data, beacons[i].create_fn);
|
|
if (res) {
|
|
/* Bail out */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
static int unprovisioned_beacon_send(void)
|
|
{
|
|
const struct bt_mesh_prov *prov;
|
|
uint8_t uri_hash[16] = { 0 };
|
|
struct net_buf *buf;
|
|
uint16_t oob_info;
|
|
|
|
LOG_DBG("");
|
|
|
|
buf = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_LOCAL_ADV,
|
|
UNPROV_XMIT, K_NO_WAIT);
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate beacon buffer");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
prov = bt_mesh_prov_get();
|
|
|
|
net_buf_add_u8(buf, BEACON_TYPE_UNPROVISIONED);
|
|
net_buf_add_mem(buf, prov->uuid, 16);
|
|
|
|
if (prov->uri && bt_mesh_s1_str(prov->uri, uri_hash) == 0) {
|
|
oob_info = prov->oob_info | BT_MESH_PROV_OOB_URI;
|
|
} else {
|
|
oob_info = prov->oob_info;
|
|
}
|
|
|
|
net_buf_add_be16(buf, oob_info);
|
|
net_buf_add_mem(buf, uri_hash, 4);
|
|
|
|
bt_mesh_adv_send(buf, NULL, NULL);
|
|
net_buf_unref(buf);
|
|
|
|
if (prov->uri) {
|
|
size_t len;
|
|
|
|
buf = bt_mesh_adv_create(BT_MESH_ADV_URI, BT_MESH_LOCAL_ADV,
|
|
UNPROV_XMIT, K_NO_WAIT);
|
|
if (!buf) {
|
|
LOG_ERR("Unable to allocate URI buffer");
|
|
return -ENOBUFS;
|
|
}
|
|
|
|
len = strlen(prov->uri);
|
|
if (net_buf_tailroom(buf) < len) {
|
|
LOG_WRN("Too long URI to fit advertising data");
|
|
} else {
|
|
net_buf_add_mem(buf, prov->uri, len);
|
|
bt_mesh_adv_send(buf, NULL, NULL);
|
|
}
|
|
|
|
net_buf_unref(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void unprovisioned_beacon_recv(struct net_buf_simple *buf)
|
|
{
|
|
const struct bt_mesh_prov *prov;
|
|
uint8_t *uuid;
|
|
uint16_t oob_info;
|
|
uint32_t uri_hash_val;
|
|
uint32_t *uri_hash = NULL;
|
|
|
|
prov = bt_mesh_prov_get();
|
|
|
|
if (!prov->unprovisioned_beacon) {
|
|
return;
|
|
}
|
|
|
|
if (buf->len != 18 && buf->len != 22) {
|
|
LOG_ERR("Invalid unprovisioned beacon length (%u)", buf->len);
|
|
return;
|
|
}
|
|
|
|
uuid = net_buf_simple_pull_mem(buf, 16);
|
|
oob_info = net_buf_simple_pull_be16(buf);
|
|
|
|
if (buf->len == 4) {
|
|
uri_hash_val = net_buf_simple_pull_be32(buf);
|
|
uri_hash = &uri_hash_val;
|
|
}
|
|
|
|
LOG_DBG("uuid %s", bt_hex(uuid, 16));
|
|
|
|
prov->unprovisioned_beacon(uuid,
|
|
(bt_mesh_prov_oob_info_t)oob_info,
|
|
uri_hash);
|
|
}
|
|
|
|
static void sub_update_beacon_observation(struct bt_mesh_subnet *sub)
|
|
{
|
|
sub->secure_beacon.last = sub->secure_beacon.cur;
|
|
sub->secure_beacon.cur = 0U;
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
sub->priv_beacon.last = sub->priv_beacon.cur;
|
|
sub->priv_beacon.cur = 0U;
|
|
#endif
|
|
}
|
|
|
|
static void update_beacon_observation(void)
|
|
{
|
|
static bool first_half;
|
|
|
|
/* Observation period is 20 seconds, whereas the beacon timer
|
|
* runs every 10 seconds. We process what's happened during the
|
|
* window only after the second half.
|
|
*/
|
|
first_half = !first_half;
|
|
if (first_half) {
|
|
return;
|
|
}
|
|
|
|
bt_mesh_subnet_foreach(sub_update_beacon_observation);
|
|
}
|
|
|
|
static bool net_beacon_is_running(void)
|
|
{
|
|
return secure_beacon_is_running() ||
|
|
(bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED);
|
|
}
|
|
|
|
static void beacon_send(struct k_work *work)
|
|
{
|
|
LOG_DBG("");
|
|
|
|
if (bt_mesh_is_provisioned()) {
|
|
if (!net_beacon_is_running()) {
|
|
return;
|
|
}
|
|
|
|
update_beacon_observation();
|
|
(void)bt_mesh_subnet_find(net_beacon_for_subnet_send, NULL);
|
|
|
|
k_work_schedule(&beacon_timer, PROVISIONED_INTERVAL);
|
|
return;
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) {
|
|
/* Don't send anything if we have an active provisioning link */
|
|
if (!bt_mesh_prov_active()) {
|
|
unprovisioned_beacon_send();
|
|
}
|
|
|
|
k_work_schedule(&beacon_timer, K_SECONDS(CONFIG_BT_MESH_UNPROV_BEACON_INT));
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (bt_mesh_beacon_auth(&keys->beacon, params->flags, keys->net_id, params->iv_index,
|
|
net_auth)) {
|
|
return false;
|
|
}
|
|
|
|
if (memcmp(params->auth, net_auth, 8)) {
|
|
LOG_WRN("Invalid auth value. Received auth: %s", bt_hex(params->auth, 8));
|
|
LOG_WRN("Calculated auth: %s", bt_hex(net_auth, 8));
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool secure_beacon_authenticate(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);
|
|
#if defined(CONFIG_BT_TESTING)
|
|
struct bt_mesh_snb beacon_info;
|
|
|
|
beacon_info.flags = params->flags;
|
|
memcpy(&beacon_info.net_id, params->net_id, 8);
|
|
beacon_info.iv_idx = params->iv_index;
|
|
memcpy(&beacon_info.auth_val, params->auth, 8);
|
|
|
|
STRUCT_SECTION_FOREACH(bt_mesh_beacon_cb, cb) {
|
|
if (cb->snb_received) {
|
|
cb->snb_received(&beacon_info);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
#if defined(CONFIG_BT_MESH_V1d1)
|
|
static bool priv_beacon_decrypt(struct bt_mesh_subnet *sub, void *cb_data)
|
|
{
|
|
struct beacon_params *params = cb_data;
|
|
uint8_t out[5];
|
|
int err;
|
|
|
|
for (int i = 0; i < ARRAY_SIZE(sub->keys); i++) {
|
|
if (!sub->keys[i].valid) {
|
|
continue;
|
|
}
|
|
|
|
err = bt_mesh_beacon_decrypt(&sub->keys[i].priv_beacon, params->random,
|
|
params->data, params->auth, out);
|
|
if (!err) {
|
|
params->new_key = (i > 0);
|
|
params->flags = out[0];
|
|
params->iv_index = sys_get_be32(&out[1]);
|
|
|
|
#if defined(CONFIG_BT_TESTING)
|
|
struct bt_mesh_prb beacon_info;
|
|
|
|
memcpy(beacon_info.random, params->random, 13);
|
|
beacon_info.flags = params->flags;
|
|
beacon_info.iv_idx = params->iv_index;
|
|
memcpy(&beacon_info.auth_tag, params->auth, 8);
|
|
|
|
STRUCT_SECTION_FOREACH(bt_mesh_beacon_cb, cb) {
|
|
if (cb->priv_received) {
|
|
cb->priv_received(&beacon_info);
|
|
}
|
|
}
|
|
#endif
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
#endif
|
|
|
|
static void net_beacon_register(struct bt_mesh_beacon *beacon, bool priv)
|
|
{
|
|
if (((priv && bt_mesh_priv_beacon_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED) ||
|
|
bt_mesh_beacon_enabled()) && beacon->cur < 0xff) {
|
|
beacon->cur++;
|
|
beacon->recv = k_uptime_get_32();
|
|
}
|
|
}
|
|
|
|
static void net_beacon_recv(struct bt_mesh_subnet *sub,
|
|
const struct beacon_params *params)
|
|
{
|
|
bt_mesh_kr_update(sub, BT_MESH_KEY_REFRESH(params->flags),
|
|
params->new_key);
|
|
|
|
/* If we have NetKey0 accept IV index initiation only from it */
|
|
if (bt_mesh_subnet_get(BT_MESH_KEY_PRIMARY) &&
|
|
sub->net_idx != BT_MESH_KEY_PRIMARY) {
|
|
LOG_WRN("Ignoring secure beacon on non-primary subnet");
|
|
return;
|
|
}
|
|
|
|
LOG_DBG("net_idx 0x%04x flags %u iv_index 0x%08x, "
|
|
"current iv_index 0x%08x",
|
|
sub->net_idx, params->flags, 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(params->flags))) {
|
|
bt_mesh_beacon_ivu_initiator(false);
|
|
}
|
|
|
|
bt_mesh_net_iv_update(params->iv_index,
|
|
BT_MESH_IV_UPDATE(params->flags));
|
|
}
|
|
|
|
static void net_beacon_resolve(struct beacon_params *params,
|
|
bool (*matcher)(struct bt_mesh_subnet *sub,
|
|
void *cb_data))
|
|
{
|
|
struct bt_mesh_subnet *sub;
|
|
struct bt_mesh_beacon *beacon;
|
|
|
|
sub = bt_mesh_subnet_find(beacon_cache_match, (void *)params);
|
|
if (sub) {
|
|
beacon = subnet_beacon_get_by_type(sub, params->private);
|
|
|
|
/* We've seen this beacon before - just update the stats */
|
|
net_beacon_register(beacon, params->private);
|
|
return;
|
|
}
|
|
|
|
sub = bt_mesh_subnet_find(matcher, params);
|
|
if (!sub) {
|
|
LOG_DBG("No subnet that matched beacon");
|
|
return;
|
|
}
|
|
|
|
if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params->new_key) {
|
|
LOG_WRN("Ignoring Phase 2 KR Update secured using old key");
|
|
return;
|
|
}
|
|
|
|
beacon = subnet_beacon_get_by_type(sub, params->private);
|
|
|
|
cache_add(params->auth, beacon);
|
|
|
|
net_beacon_recv(sub, params);
|
|
net_beacon_register(beacon, params->private);
|
|
}
|
|
|
|
static void secure_beacon_recv(struct net_buf_simple *buf)
|
|
{
|
|
struct beacon_params params;
|
|
|
|
if (buf->len < 21) {
|
|
LOG_ERR("Too short secure beacon (len %u)", buf->len);
|
|
return;
|
|
}
|
|
|
|
params.private = false;
|
|
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;
|
|
|
|
net_beacon_resolve(¶ms, secure_beacon_authenticate);
|
|
}
|
|
|
|
#if defined(CONFIG_BT_MESH_V1d1)
|
|
static void private_beacon_recv(struct net_buf_simple *buf)
|
|
{
|
|
struct beacon_params params;
|
|
|
|
if (buf->len < 26) {
|
|
LOG_ERR("Too short private beacon (len %u)", buf->len);
|
|
return;
|
|
}
|
|
|
|
params.private = true;
|
|
params.random = net_buf_simple_pull_mem(buf, 13);
|
|
params.data = net_buf_simple_pull_mem(buf, 5);
|
|
params.auth = buf->data;
|
|
|
|
net_beacon_resolve(¶ms, priv_beacon_decrypt);
|
|
}
|
|
#endif
|
|
|
|
void bt_mesh_beacon_recv(struct net_buf_simple *buf)
|
|
{
|
|
uint8_t type;
|
|
|
|
LOG_DBG("%u bytes: %s", buf->len, bt_hex(buf->data, buf->len));
|
|
|
|
if (buf->len < 1) {
|
|
LOG_ERR("Too short beacon");
|
|
return;
|
|
}
|
|
|
|
type = net_buf_simple_pull_u8(buf);
|
|
switch (type) {
|
|
case BEACON_TYPE_UNPROVISIONED:
|
|
if (IS_ENABLED(CONFIG_BT_MESH_PB_ADV)) {
|
|
unprovisioned_beacon_recv(buf);
|
|
}
|
|
break;
|
|
case BEACON_TYPE_SECURE:
|
|
secure_beacon_recv(buf);
|
|
break;
|
|
case BEACON_TYPE_PRIVATE:
|
|
#if defined(CONFIG_BT_MESH_V1d1)
|
|
private_beacon_recv(buf);
|
|
#endif
|
|
break;
|
|
default:
|
|
LOG_WRN("Unknown beacon type 0x%02x", type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void bt_mesh_beacon_update(struct bt_mesh_subnet *sub)
|
|
{
|
|
uint8_t flags = bt_mesh_net_flags(sub);
|
|
struct bt_mesh_subnet_keys *keys;
|
|
|
|
keys = &sub->keys[SUBNET_KEY_TX_IDX(sub)];
|
|
|
|
LOG_DBG("NetIndex 0x%03x Using %s key", sub->net_idx,
|
|
SUBNET_KEY_TX_IDX(sub) ? "new" : "current");
|
|
LOG_DBG("flags 0x%02x, IVI 0x%08x", flags, bt_mesh.iv_index);
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
/* Invalidate private beacon to force regeneration: */
|
|
sub->priv_beacon_ctx.idx = priv_random.idx - 1;
|
|
priv_random.timestamp = 0;
|
|
#endif
|
|
|
|
bt_mesh_beacon_auth(&keys->beacon, flags, keys->net_id, bt_mesh.iv_index,
|
|
sub->secure_beacon.auth);
|
|
}
|
|
|
|
static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt)
|
|
{
|
|
if (evt != BT_MESH_KEY_DELETED) {
|
|
bt_mesh_beacon_update(sub);
|
|
}
|
|
}
|
|
|
|
BT_MESH_SUBNET_CB_DEFINE(beacon) = {
|
|
.evt_handler = subnet_evt,
|
|
};
|
|
|
|
void bt_mesh_beacon_init(void)
|
|
{
|
|
k_work_init_delayable(&beacon_timer, beacon_send);
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
private_random_update();
|
|
#endif
|
|
}
|
|
|
|
void bt_mesh_beacon_ivu_initiator(bool enable)
|
|
{
|
|
atomic_set_bit_to(bt_mesh.flags, BT_MESH_IVU_INITIATOR, enable);
|
|
|
|
/* Fire the beacon handler straight away if it's not already pending -
|
|
* in which case we'll fire according to the ongoing periodic sending.
|
|
* If beacons are disabled, the handler will exit early.
|
|
*
|
|
* An alternative solution would be to check whether beacons are enabled
|
|
* here, and cancel if not. As the cancel operation may fail, we would
|
|
* still have to implement an early exit mechanism, so we might as well
|
|
* just use this every time.
|
|
*/
|
|
k_work_schedule(&beacon_timer, K_NO_WAIT);
|
|
}
|
|
|
|
static void subnet_beacon_enable(struct bt_mesh_subnet *sub)
|
|
{
|
|
sub->secure_beacon.last = 0U;
|
|
sub->secure_beacon.cur = 0U;
|
|
|
|
#if defined(CONFIG_BT_MESH_PRIV_BEACONS)
|
|
sub->priv_beacon.last = 0U;
|
|
sub->priv_beacon.cur = 0U;
|
|
#endif
|
|
|
|
bt_mesh_beacon_update(sub);
|
|
}
|
|
|
|
void bt_mesh_beacon_enable(void)
|
|
{
|
|
if (bt_mesh_is_provisioned()) {
|
|
bt_mesh_subnet_foreach(subnet_beacon_enable);
|
|
}
|
|
|
|
k_work_reschedule(&beacon_timer, K_NO_WAIT);
|
|
}
|
|
|
|
void bt_mesh_beacon_disable(void)
|
|
{
|
|
/* If this fails, we'll do an early exit in the work handler. */
|
|
(void)k_work_cancel_delayable(&beacon_timer);
|
|
}
|
|
|
|
void bt_mesh_beacon_priv_random_get(uint8_t *random, size_t size)
|
|
{
|
|
__ASSERT(size <= sizeof(priv_random.val), "Invalid random value size %u", size);
|
|
memcpy(random, priv_random.val, size);
|
|
}
|