/* * Copyright (c) 2017 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include "common/bt_str.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 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 bt_mesh_subnet *beacon_send_sub_curr; #if defined(CONFIG_BT_MESH_PRIV_BEACONS) 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; #endif 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_start(uint16_t duration, int err, void *user_data) { if (err) { LOG_ERR("Failed to send beacon: err %d", err); if (beacon_send_sub_curr) { k_work_reschedule(&beacon_timer, K_NO_WAIT); } } } 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(); if (beacon_send_sub_curr) { k_work_reschedule(&beacon_timer, K_MSEC(20)); } } 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 int net_beacon_send(struct bt_mesh_subnet *sub, struct bt_mesh_beacon *beacon, int (*beacon_create)(struct bt_mesh_subnet *sub, struct net_buf_simple *buf)) { static const struct bt_mesh_send_cb send_cb = { .start = beacon_start, .end = beacon_complete, }; uint32_t now = k_uptime_get_32(); struct bt_mesh_adv *adv; 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 -ENOMSG; } adv = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_ADV_TAG_LOCAL, PROV_XMIT, K_NO_WAIT); if (!adv) { LOG_ERR("Unable to allocate beacon adv"); return -ENOMEM; /* Bail out */ } err = beacon_create(sub, &adv->b); if (!err) { bt_mesh_adv_send(adv, &send_cb, beacon); } bt_mesh_adv_unref(adv); return err; } static int net_beacon_for_subnet_send(struct bt_mesh_subnet *sub) { int err = -ENOMSG; 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; } err = net_beacon_send(sub, beacons[i].beacon, beacons[i].create_fn); if (err < 0) { /* Bail out */ break; } } return err; } static int unprovisioned_beacon_send(void) { const struct bt_mesh_prov *prov; uint8_t uri_hash[16] = { 0 }; struct bt_mesh_adv *adv; uint16_t oob_info; LOG_DBG(""); adv = bt_mesh_adv_create(BT_MESH_ADV_BEACON, BT_MESH_ADV_TAG_LOCAL, UNPROV_XMIT, K_NO_WAIT); if (!adv) { LOG_ERR("Unable to allocate beacon adv"); return -ENOBUFS; } prov = bt_mesh_prov_get(); net_buf_simple_add_u8(&adv->b, BEACON_TYPE_UNPROVISIONED); net_buf_simple_add_mem(&adv->b, 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_simple_add_be16(&adv->b, oob_info); net_buf_simple_add_mem(&adv->b, uri_hash, 4); bt_mesh_adv_send(adv, NULL, NULL); bt_mesh_adv_unref(adv); if (prov->uri) { size_t len; adv = bt_mesh_adv_create(BT_MESH_ADV_URI, BT_MESH_ADV_TAG_LOCAL, UNPROV_XMIT, K_NO_WAIT); if (!adv) { LOG_ERR("Unable to allocate URI adv"); return -ENOBUFS; } len = strlen(prov->uri); if (net_buf_simple_tailroom(&adv->b) < len) { LOG_WRN("Too long URI to fit advertising data"); } else { net_buf_simple_add_mem(&adv->b, prov->uri, len); bt_mesh_adv_send(adv, NULL, NULL); } bt_mesh_adv_unref(adv); } 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 bool beacons_send_next(void) { int err; struct bt_mesh_subnet *sub_first = bt_mesh_subnet_next(NULL); struct bt_mesh_subnet *sub_next; do { sub_next = bt_mesh_subnet_next(beacon_send_sub_curr); if (sub_next == sub_first && beacon_send_sub_curr != NULL) { beacon_send_sub_curr = NULL; return false; } beacon_send_sub_curr = sub_next; err = net_beacon_for_subnet_send(beacon_send_sub_curr); if (err < 0 && (err != -ENOMSG)) { LOG_ERR("Failed to advertise subnet %d: err %d", beacon_send_sub_curr->net_idx, err); } } while (err); return true; } static void beacon_send(struct k_work *work) { LOG_DBG(""); if (bt_mesh_is_provisioned()) { if (!net_beacon_is_running()) { return; } if (!beacon_send_sub_curr) { update_beacon_observation(); } if (!beacons_send_next()) { 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; } 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; } 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); } 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); } 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: private_beacon_recv(buf); 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. */ beacon_send_sub_curr = NULL; 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); } beacon_send_sub_curr = NULL; 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. */ beacon_send_sub_curr = NULL; (void)k_work_cancel_delayable(&beacon_timer); }