From f9b19010ed06a18e81b5201474428b5fd0a99f7f Mon Sep 17 00:00:00 2001 From: Trond Einar Snekvik Date: Tue, 2 Jun 2020 12:04:53 +0200 Subject: [PATCH] Bluetooth: Mesh: Private Beacons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for private beacon sending and receiving. Co-authored-by: Krzysztof Kopyściński Co-authored-by: Pavel Vasilyev Signed-off-by: Trond Einar Snekvik Signed-off-by: Pavel Vasilyev --- .../bluetooth/api/mesh/models.rst | 6 +- .../bluetooth/api/mesh/priv_beacon_cli.rst | 36 ++ .../bluetooth/api/mesh/priv_beacon_srv.rst | 39 ++ include/zephyr/bluetooth/mesh.h | 2 + include/zephyr/bluetooth/mesh/access.h | 2 + include/zephyr/bluetooth/mesh/cfg.h | 113 ++++++ .../zephyr/bluetooth/mesh/priv_beacon_cli.h | 145 ++++++++ .../zephyr/bluetooth/mesh/priv_beacon_srv.h | 43 +++ include/zephyr/bluetooth/mesh/proxy.h | 10 + subsys/bluetooth/mesh/CMakeLists.txt | 4 + subsys/bluetooth/mesh/Kconfig | 21 ++ subsys/bluetooth/mesh/beacon.c | 339 ++++++++++++++---- subsys/bluetooth/mesh/beacon.h | 5 +- subsys/bluetooth/mesh/cfg.c | 99 ++++- subsys/bluetooth/mesh/cfg_srv.c | 7 + subsys/bluetooth/mesh/crypto.c | 123 +++++++ subsys/bluetooth/mesh/crypto.h | 13 + subsys/bluetooth/mesh/foundation.h | 10 + subsys/bluetooth/mesh/net.c | 17 +- subsys/bluetooth/mesh/net.h | 7 + subsys/bluetooth/mesh/priv_beacon_cli.c | 303 ++++++++++++++++ subsys/bluetooth/mesh/priv_beacon_srv.c | 189 ++++++++++ subsys/bluetooth/mesh/proxy.h | 8 +- subsys/bluetooth/mesh/proxy_srv.c | 214 +++++++++-- subsys/bluetooth/mesh/subnet.c | 67 +++- subsys/bluetooth/mesh/subnet.h | 14 +- 26 files changed, 1711 insertions(+), 125 deletions(-) create mode 100644 doc/connectivity/bluetooth/api/mesh/priv_beacon_cli.rst create mode 100644 doc/connectivity/bluetooth/api/mesh/priv_beacon_srv.rst create mode 100644 include/zephyr/bluetooth/mesh/priv_beacon_cli.h create mode 100644 include/zephyr/bluetooth/mesh/priv_beacon_srv.h create mode 100644 subsys/bluetooth/mesh/priv_beacon_cli.c create mode 100644 subsys/bluetooth/mesh/priv_beacon_srv.c diff --git a/doc/connectivity/bluetooth/api/mesh/models.rst b/doc/connectivity/bluetooth/api/mesh/models.rst index 07e401fb3b1..2b9cab610a1 100644 --- a/doc/connectivity/bluetooth/api/mesh/models.rst +++ b/doc/connectivity/bluetooth/api/mesh/models.rst @@ -6,7 +6,7 @@ Mesh models Foundation models ***************** -The Bluetooth mesh specification defines four foundation models that can be +The Bluetooth mesh specification defines six foundation models that can be used by network administrators to configure and diagnose mesh nodes. .. toctree:: @@ -16,11 +16,13 @@ used by network administrators to configure and diagnose mesh nodes. cfg_cli health_srv health_cli + priv_beacon_srv + priv_beacon_cli Model specification models ************************** -In addition to the Foundation models defined in the Bluetooth Mesh specification, the Bluetooth Mesh Model specification defines several models, some of which are implemented in Zephyr: +In addition to the foundation models defined in the Bluetooth mesh specification, the Bluetooth Mesh Model Specification defines several models, some of which are implemented in Zephyr: .. toctree:: :maxdepth: 1 diff --git a/doc/connectivity/bluetooth/api/mesh/priv_beacon_cli.rst b/doc/connectivity/bluetooth/api/mesh/priv_beacon_cli.rst new file mode 100644 index 00000000000..6a6f44b1387 --- /dev/null +++ b/doc/connectivity/bluetooth/api/mesh/priv_beacon_cli.rst @@ -0,0 +1,36 @@ +.. _bluetooth_mesh_models_priv_beacon_cli: + +Private Beacon Client +##################### + +The Private Beacon Client model is a foundation model defined by the Bluetooth +mesh specification. It is enabled with the +:kconfig:option:`CONFIG_BT_MESH_PRIV_BEACON_CLI` option. + +The Private Beacon Client model is introduced in the Bluetooth Mesh Profile +Specification version 1.1, and provides functionality for configuring the +:ref:`bluetooth_mesh_models_priv_beacon_srv` models. + +The Private Beacons feature adds privacy to the different Bluetooth mesh +beacons by periodically randomizing the beacon input data. This protects the +mesh node from being tracked by devices outside the mesh network, and hides the +network's IV index, IV update and the Key Refresh state. + +The Private Beacon Client model communicates with a +:ref:`bluetooth_mesh_models_priv_beacon_srv` model using the device key of the +target node. The Private Beacon Client model may communicate with servers on +other nodes or self-configure through the local Private Beacon Server model. + +All configuration functions in the Private Beacon Client API have ``net_idx`` +and ``addr`` as their first parameters. These should be set to the network +index and the primary unicast address the target node was provisioned with. + +The Private Beacon Client model is optional, and can be instantiated on any +element. + +API reference +************* + +.. doxygengroup:: bt_mesh_priv_beacon_cli + :project: Zephyr + :members: diff --git a/doc/connectivity/bluetooth/api/mesh/priv_beacon_srv.rst b/doc/connectivity/bluetooth/api/mesh/priv_beacon_srv.rst new file mode 100644 index 00000000000..d69fe616e38 --- /dev/null +++ b/doc/connectivity/bluetooth/api/mesh/priv_beacon_srv.rst @@ -0,0 +1,39 @@ +.. _bluetooth_mesh_models_priv_beacon_srv: + +Private Beacon Server +##################### + +The Private Beacon Server model is a foundation model defined by the Bluetooth +mesh specification. It is enabled with +:kconfig:option:`CONFIG_BT_MESH_PRIV_BEACON_SRV` option. + +The Private Beacon Server model is introduced in the Bluetooth Mesh Profile +Specification version 1.1, and controls the mesh node's Private Beacon state, +Private GATT Proxy state and Private Node Identity state. + +The Private Beacons feature adds privacy to the different Bluetooth mesh +beacons by periodically randomizing the beacon input data. This protects the +mesh node from being tracked by devices outside the mesh network, and hides the +network's IV index, IV update and the Key Refresh state. The Private Beacon Server +must be instantiated for the device to support sending of the private beacons, +but the node will process received private beacons without it. + +The Private Beacon Server does not have an API of its own, but relies on a +:ref:`bluetooth_mesh_models_priv_beacon_cli` to control it. The Private Beacon +Server model only accepts messages encrypted with the node's device key. + +The application can configure the initial parameters of the Private Beacon +Server model through the :c:struct:`bt_mesh_priv_beacon_srv` instance passed to +:c:macro:`BT_MESH_MODEL_PRIV_BEACON_SRV`. Note that if the mesh node stored +changes to this configuration in the settings subsystem, the initial values may +be overwritten upon loading. + +The Private Beacon Server model is optional, and can only be instantiated in the +node's primary element. + +API reference +************* + +.. doxygengroup:: bt_mesh_priv_beacon_srv + :project: Zephyr + :members: diff --git a/include/zephyr/bluetooth/mesh.h b/include/zephyr/bluetooth/mesh.h index 8c65ae2fb6a..8ca67c8d2fc 100644 --- a/include/zephyr/bluetooth/mesh.h +++ b/include/zephyr/bluetooth/mesh.h @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include #include diff --git a/include/zephyr/bluetooth/mesh/access.h b/include/zephyr/bluetooth/mesh/access.h index 2f18892b811..7915d338f48 100644 --- a/include/zephyr/bluetooth/mesh/access.h +++ b/include/zephyr/bluetooth/mesh/access.h @@ -128,6 +128,8 @@ struct bt_mesh_elem { #define BT_MESH_MODEL_ID_HEALTH_CLI 0x0003 #define BT_MESH_MODEL_ID_REMOTE_PROV_SRV 0x0004 #define BT_MESH_MODEL_ID_REMOTE_PROV_CLI 0x0005 +#define BT_MESH_MODEL_ID_PRIV_BEACON_SRV 0x000a +#define BT_MESH_MODEL_ID_PRIV_BEACON_CLI 0x000b #define BT_MESH_MODEL_ID_SAR_CFG_SRV 0x000e #define BT_MESH_MODEL_ID_SAR_CFG_CLI 0x000f #define BT_MESH_MODEL_ID_LARGE_COMP_DATA_SRV 0x0012 diff --git a/include/zephyr/bluetooth/mesh/cfg.h b/include/zephyr/bluetooth/mesh/cfg.h index f63fbb652ec..e16d3032f23 100644 --- a/include/zephyr/bluetooth/mesh/cfg.h +++ b/include/zephyr/bluetooth/mesh/cfg.h @@ -49,10 +49,17 @@ enum bt_mesh_feat_state { #define BT_MESH_BEACON_DISABLED BT_MESH_FEATURE_DISABLED #define BT_MESH_BEACON_ENABLED BT_MESH_FEATURE_ENABLED +#define BT_MESH_PRIV_BEACON_DISABLED BT_MESH_FEATURE_DISABLED +#define BT_MESH_PRIV_BEACON_ENABLED BT_MESH_FEATURE_ENABLED + #define BT_MESH_GATT_PROXY_DISABLED BT_MESH_FEATURE_DISABLED #define BT_MESH_GATT_PROXY_ENABLED BT_MESH_FEATURE_ENABLED #define BT_MESH_GATT_PROXY_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED +#define BT_MESH_PRIV_GATT_PROXY_DISABLED BT_MESH_FEATURE_DISABLED +#define BT_MESH_PRIV_GATT_PROXY_ENABLED BT_MESH_FEATURE_ENABLED +#define BT_MESH_PRIV_GATT_PROXY_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED + #define BT_MESH_FRIEND_DISABLED BT_MESH_FEATURE_DISABLED #define BT_MESH_FRIEND_ENABLED BT_MESH_FEATURE_ENABLED #define BT_MESH_FRIEND_NOT_SUPPORTED BT_MESH_FEATURE_NOT_SUPPORTED @@ -73,6 +80,50 @@ void bt_mesh_beacon_set(bool beacon); */ bool bt_mesh_beacon_enabled(void); +/** @brief Enable or disable sending of the Mesh Private beacon. + * + * Support for the Private beacon state must be enabled with @c + * CONFIG_BT_MESH_PRIV_BEACONS. + * + * @param priv_beacon New Mesh Private beacon state. Must be one of + * @ref BT_MESH_FEATURE_ENABLED and + * @ref BT_MESH_FEATURE_DISABLED. + * + * @retval 0 Successfully changed the Mesh Private beacon feature state. + * @retval -ENOTSUP The Mesh Private beacon feature is not supported. + * @retval -EINVAL Invalid parameter. + * @retval -EALREADY Already in the given state. + */ +int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon); + +/** @brief Get the current Mesh Private beacon state. + * + * @returns The Mesh Private beacon feature state. + */ +enum bt_mesh_feat_state bt_mesh_priv_beacon_get(void); + +/** @brief Set the current Mesh Private beacon update interval. + * + * The Mesh Private beacon's randomization value is updated regularly to + * maintain the node's privacy. The update interval controls how often + * the beacon is updated, in 10 second increments. + * + * @param interval Private beacon update interval in 10 second steps, or 0 to + * update on every beacon transmission. + */ +void bt_mesh_priv_beacon_update_interval_set(uint8_t interval); + +/** @brief Get the current Mesh Private beacon update interval. + * + * The Mesh Private beacon's randomization value is updated regularly to + * maintain the node's privacy. The update interval controls how often + * the beacon is updated, in 10 second increments. + * + * @returns The Private beacon update interval in 10 second steps, or 0 if + * the beacon is updated every time it's transmitted. + */ +uint8_t bt_mesh_priv_beacon_update_interval_get(void); + /** @brief Set the default TTL value. * * The default TTL value is used when no explicit TTL value is set. Models will @@ -181,6 +232,29 @@ int bt_mesh_gatt_proxy_set(enum bt_mesh_feat_state gatt_proxy); */ enum bt_mesh_feat_state bt_mesh_gatt_proxy_get(void); +/** @brief Enable or disable the Private GATT Proxy feature. + * + * Support for the Private GATT Proxy feature must be enabled through the + * @kconfig{CONFIG_BT_MESH_PRIV_BEACONS} and @kconfig{CONFIG_BT_MESH_GATT_PROXY} + * configuration options. + * + * @param priv_gatt_proxy New Private GATT Proxy state. Must be one of + * @ref BT_MESH_FEATURE_ENABLED and + * @ref BT_MESH_FEATURE_DISABLED. + * + * @retval 0 Successfully changed the Private GATT Proxy feature state. + * @retval -ENOTSUP The Private GATT Proxy feature is not supported. + * @retval -EINVAL Invalid parameter. + * @retval -EALREADY Already in the given state. + */ +int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy); + +/** @brief Get the current Private GATT Proxy state. + * + * @returns The Private GATT Proxy feature state. + */ +enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void); + /** @brief Enable or disable the Friend feature. * * Any active friendships will be terminated immediately if the Friend feature @@ -341,6 +415,45 @@ uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx, uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx, enum bt_mesh_feat_state *node_id); +/** @brief Set the Private Node Identity state of the Subnet. + * + * The Private Node Identity state of a Subnet determines whether the Subnet + * advertises connectable Private Node Identity beacons for Proxy Clients to + * connect to. Once started, the Node Identity beacon runs for 60 seconds, + * or until it is stopped. + * + * Private Node Identity can only be enabled if regular Node Identity is not + * enabled for any subnet. + * + * GATT Proxy and Private Beacon support must be enabled through + * @kconfig{CONFIG_BT_MESH_GATT_PROXY} and + * @kconfig{CONFIG_BT_MESH_PRIV_BEACONS}. + * + * @param net_idx Network index. + * @param priv_node_id New Private Node Identity state, must be either @ref + * BT_MESH_FEATURE_ENABLED or @ref BT_MESH_FEATURE_DISABLED. + * + * @retval STATUS_SUCCESS Successfully set the Private Node Identity state of the Subnet. + * @retval STATUS_INVALID_NETKEY The NetIdx is unknown. + * @retval STATUS_FEAT_NOT_SUPP The Private Node Identity feature is not supported. + * @retval STATUS_TEMP_STATE_CHG_FAIL The Private Node Identity state cannot be enabled, because + * Node Identity state is already enabled. + * @retval STATUS_CANNOT_SET Couldn't set the Private Node Identity state. + */ +uint8_t bt_mesh_subnet_priv_node_id_set(uint16_t net_idx, + enum bt_mesh_feat_state priv_node_id); + +/** @brief Get the Private Node Identity state of the Subnet. + * + * @param net_idx Network index. + * @param priv_node_id Private Node Identity variable to fill. + * + * @retval STATUS_SUCCESS Successfully populated the @c priv_node_id variable. + * @retval STATUS_INVALID_NETKEY The NetIdx is unknown. + */ +uint8_t bt_mesh_subnet_priv_node_id_get(uint16_t net_idx, + enum bt_mesh_feat_state *priv_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. diff --git a/include/zephyr/bluetooth/mesh/priv_beacon_cli.h b/include/zephyr/bluetooth/mesh/priv_beacon_cli.h new file mode 100644 index 00000000000..d801a074a2f --- /dev/null +++ b/include/zephyr/bluetooth/mesh/priv_beacon_cli.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_priv_beacon_cli Bluetooth Mesh Private Beacon Client + * @{ + * @brief + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Private Beacon Client model composition data entry. + * + * @param cli_data Pointer to a @ref bt_mesh_priv_beacon_cli instance. + */ +#define BT_MESH_MODEL_PRIV_BEACON_CLI(cli_data) \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_PRIV_BEACON_CLI, \ + bt_mesh_priv_beacon_cli_op, NULL, cli_data, \ + &bt_mesh_priv_beacon_cli_cb) + +/** Mesh Private Beacon Client model */ +struct bt_mesh_priv_beacon_cli { + /** Timeout value in milliseconds. */ + int32_t timeout; + struct bt_mesh_model *mod; + + /* Internal parameters for tracking message responses. */ + struct bt_mesh_msg_ack_ctx ack_ctx; +}; + +/** Private Beacon */ +struct bt_mesh_priv_beacon { + /** Private beacon is enabled */ + uint8_t enabled; + /** Random refresh interval (in 10 second steps), or 0 to keep current + * value. + */ + uint8_t rand_interval; +}; + +/** Private Node Identity */ +struct bt_mesh_priv_node_id { + /** Index of the NetKey. */ + uint16_t net_idx; + /** Private Node Identity state */ + uint8_t state; + /** Response status code. */ + uint8_t status; +}; + +/** @brief Set the target's Private Beacon state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param val New Private Beacon value. Returns response status on success. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_set(uint16_t net_idx, uint16_t addr, + struct bt_mesh_priv_beacon *val); + +/** @brief Get the target's Private Beacon state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param val Response buffer for Private Beacon value. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_get(uint16_t net_idx, uint16_t addr, + struct bt_mesh_priv_beacon *val); + +/** @brief Set the target's Private GATT Proxy state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param val New Private GATT Proxy value. Returns response status on + * success. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_gatt_proxy_set(uint16_t net_idx, uint16_t addr, + uint8_t *val); + +/** @brief Get the target's Private GATT Proxy state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param val Response buffer for Private GATT Proxy value. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_gatt_proxy_get(uint16_t net_idx, uint16_t addr, + uint8_t *val); + +/** @brief Set the target's Private Node Identity state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param val New Private Node Identity value. Returns response status on + * success. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_node_id_set(uint16_t net_idx, uint16_t addr, + struct bt_mesh_priv_node_id *val); + +/** @brief Get the target's Private Node Identity state. + * + * @param net_idx Network index to encrypt with. + * @param addr Target node address. + * @param key_net_idx Network index to get the Private Node Identity state of. + * @param val Response buffer for Private Node Identity value. + * + * @return 0 on success, or (negative) error code otherwise. + */ +int bt_mesh_priv_beacon_cli_node_id_get(uint16_t net_idx, uint16_t addr, + uint16_t key_net_idx, + struct bt_mesh_priv_node_id *val); + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op bt_mesh_priv_beacon_cli_op[]; +extern const struct bt_mesh_model_cb bt_mesh_priv_beacon_cli_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_CLI_H__ */ + +/** @} */ diff --git a/include/zephyr/bluetooth/mesh/priv_beacon_srv.h b/include/zephyr/bluetooth/mesh/priv_beacon_srv.h new file mode 100644 index 00000000000..5eb694141ff --- /dev/null +++ b/include/zephyr/bluetooth/mesh/priv_beacon_srv.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @defgroup bt_mesh_priv_beacon_srv Bluetooth Mesh Private Beacon Server + * @{ + * @brief + */ + +#ifndef ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__ +#define ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * + * @brief Private Beacon Server model composition data entry. + */ +#define BT_MESH_MODEL_PRIV_BEACON_SRV \ + BT_MESH_MODEL_CB(BT_MESH_MODEL_ID_PRIV_BEACON_SRV, \ + bt_mesh_priv_beacon_srv_op, NULL, NULL, \ + &bt_mesh_priv_beacon_srv_cb) + +/** @cond INTERNAL_HIDDEN */ +extern const struct bt_mesh_model_op bt_mesh_priv_beacon_srv_op[]; +extern const struct bt_mesh_model_cb bt_mesh_priv_beacon_srv_cb; +/** @endcond */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_BLUETOOTH_MESH_PRIV_BEACON_SRV_H__ */ + +/** @} */ diff --git a/include/zephyr/bluetooth/mesh/proxy.h b/include/zephyr/bluetooth/mesh/proxy.h index 74d83f6b01e..4595e5d8017 100644 --- a/include/zephyr/bluetooth/mesh/proxy.h +++ b/include/zephyr/bluetooth/mesh/proxy.h @@ -62,6 +62,16 @@ struct bt_mesh_proxy_cb { */ int bt_mesh_proxy_identity_enable(void); +/** @brief Enable advertising with Private Node Identity. + * + * This API requires that GATT Proxy support has been enabled. Once called + * each subnet will start advertising using Private Node Identity for the next + * 60 seconds. + * + * @return 0 on success, or (negative) error code on failure. + */ +int bt_mesh_proxy_private_identity_enable(void); + /** @brief Allow Proxy Client to auto connect to a network. * * This API allows a proxy client to auto-connect a given network. diff --git a/subsys/bluetooth/mesh/CMakeLists.txt b/subsys/bluetooth/mesh/CMakeLists.txt index 9e35b57f1ec..184afb0d57e 100644 --- a/subsys/bluetooth/mesh/CMakeLists.txt +++ b/subsys/bluetooth/mesh/CMakeLists.txt @@ -73,6 +73,10 @@ zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_SRV large_comp_data zephyr_library_sources_ifdef(CONFIG_BT_MESH_LARGE_COMP_DATA_CLI large_comp_data_cli.c) +zephyr_library_sources_ifdef(CONFIG_BT_MESH_PRIV_BEACON_SRV priv_beacon_srv.c) + +zephyr_library_sources_ifdef(CONFIG_BT_MESH_PRIV_BEACON_CLI priv_beacon_cli.c) + zephyr_library_sources_ifdef(CONFIG_BT_MESH_SELF_TEST test.c) zephyr_library_sources_ifdef(CONFIG_BT_MESH_CDB cdb.c) diff --git a/subsys/bluetooth/mesh/Kconfig b/subsys/bluetooth/mesh/Kconfig index b414bf9cd17..79df427237f 100644 --- a/subsys/bluetooth/mesh/Kconfig +++ b/subsys/bluetooth/mesh/Kconfig @@ -446,6 +446,7 @@ config BT_MESH_ADV_STACK_SIZE int "Mesh advertiser thread stack size" depends on BT_MESH_ADV_LEGACY default 1024 if BT_HOST_CRYPTO + default 776 if BT_MESH_PRIV_BEACONS default 768 help Size of bt mesh adv thread stack. @@ -1212,6 +1213,26 @@ config BT_MESH_LARGE_COMP_DATA_CLI help Enable support for the Large Composition Data Client model. +config BT_MESH_PRIV_BEACONS + bool "Support for private beacons" + default y + help + Enable support for private beacons. + +if BT_MESH_PRIV_BEACONS + +config BT_MESH_PRIV_BEACON_SRV + bool "Support for Private Beacon Server Model" + help + Enable support for the Private Beacon Server model. + +config BT_MESH_PRIV_BEACON_CLI + bool "Support for Private Beacon Client Model" + help + Enable support for the Private Beacon Client model. + +endif # BT_MESH_PRIV_BEACONS + menu "Transport SAR configuration" config BT_MESH_SAR_TX_SEG_INT_STEP diff --git a/subsys/bluetooth/mesh/beacon.c b/subsys/bluetooth/mesh/beacon.c index 11634f44cce..a32caa19433 100644 --- a/subsys/bluetooth/mesh/beacon.c +++ b/subsys/bluetooth/mesh/beacon.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -22,7 +23,7 @@ #include "prov.h" #include "crypto.h" #include "beacon.h" -#include "foundation.h" +#include "cfg.h" #define LOG_LEVEL CONFIG_BT_MESH_BEACON_LOG_LEVEL #include @@ -32,6 +33,7 @@ LOG_MODULE_REGISTER(bt_mesh_beacon); #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) @@ -40,20 +42,39 @@ LOG_MODULE_REGISTER(bt_mesh_beacon); #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; -static bool beacon_cache_match(struct bt_mesh_subnet *sub, void *beacon_data) +#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 bool beacon_cache_match(struct bt_mesh_subnet *sub, void *auth) { - return !memcmp(sub->beacon_cache, beacon_data, 21); + return !memcmp(sub->beacon_cache, auth, sizeof(sub->beacon_cache)); } -static void cache_add(uint8_t data[21], struct bt_mesh_subnet *sub) +static void cache_add(const uint8_t auth[8], struct bt_mesh_subnet *sub) { - memcpy(sub->beacon_cache, data, 21); + memcpy(sub->beacon_cache, auth, sizeof(sub->beacon_cache)); } void bt_mesh_beacon_cache_clear(struct bt_mesh_subnet *sub) { - (void)memset(sub->beacon_cache, 0, 21); + (void)memset(sub->beacon_cache, 0, sizeof(sub->beacon_cache)); } static void beacon_complete(int err, void *user_data) @@ -65,8 +86,8 @@ static void beacon_complete(int err, void *user_data) sub->beacon_sent = k_uptime_get_32(); } -void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, - struct net_buf_simple *buf) +static void 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; @@ -90,11 +111,99 @@ void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, LOG_DBG("IV Index 0x%08x Auth %s", bt_mesh.iv_index, bt_hex(sub->auth, 8)); } +#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 (interval && + uptime - priv_random.timestamp < (10 * interval * MSEC_PER_SEC)) { + /* 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.data, + sub->auth); + if (err) { + return err; + } + + sub->priv_beacon.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.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.data, 5); + net_buf_simple_add_mem(buf, sub->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) +{ +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + if (bt_mesh_priv_beacon_get() == BT_MESH_FEATURE_ENABLED) { + 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(sub) \ ((10 * ((sub)->beacons_last + 1)) * MSEC_PER_SEC - (5 * MSEC_PER_SEC)) -static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data) +static bool net_beacon_send(struct bt_mesh_subnet *sub, void *cb_data) { static const struct bt_mesh_send_cb send_cb = { .end = beacon_complete, @@ -103,6 +212,7 @@ static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data) struct net_buf *buf; uint32_t time_diff; uint32_t time_since_last_recv; + int err; LOG_DBG(""); @@ -121,7 +231,10 @@ static bool secure_beacon_send(struct bt_mesh_subnet *sub, void *cb_data) return true; /* Bail out */ } - bt_mesh_beacon_create(sub, &buf->b); + err = bt_mesh_beacon_create(sub, &buf->b); + if (err) { + return true; /* Bail out */ + } bt_mesh_adv_send(buf, &send_cb, sub); net_buf_unref(buf); @@ -242,18 +355,24 @@ static void update_beacon_observation(void) bt_mesh_subnet_foreach(sub_update_beacon_observation); } +static bool net_beacon_is_running(void) +{ + return bt_mesh_beacon_enabled() || + atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR) || + (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 (!bt_mesh_beacon_enabled() && - !atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { + if (!net_beacon_is_running()) { return; } update_beacon_observation(); - (void)bt_mesh_subnet_find(secure_beacon_send, NULL); + (void)bt_mesh_subnet_find(net_beacon_send, NULL); k_work_schedule(&beacon_timer, PROVISIONED_INTERVAL); return; @@ -271,7 +390,13 @@ static void beacon_send(struct k_work *work) } struct beacon_params { - const uint8_t *net_id; + union { + const uint8_t *net_id; + struct { + const uint8_t *data; + const uint8_t *random; + } private; + }; const uint8_t *auth; uint32_t iv_index; uint8_t flags; @@ -300,7 +425,7 @@ static bool auth_match(struct bt_mesh_subnet_keys *keys, return true; } -static bool subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data) +static bool secure_beacon_authenticate(struct bt_mesh_subnet *sub, void *cb_data) { struct beacon_params *params = cb_data; @@ -314,74 +439,128 @@ static bool subnet_by_id(struct bt_mesh_subnet *sub, void *cb_data) 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->private.random, + params->private.data, params->auth, + out); + if (!err) { + params->new_key = (i > 0); + params->flags = out[0]; + params->iv_index = sys_get_be32(&out[1]); + return true; + } + } + + return false; +} + +static void net_beacon_register(struct bt_mesh_subnet *sub) +{ + if (bt_mesh_beacon_enabled() && sub->beacons_cur < 0xff) { + sub->beacons_cur++; + sub->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; + + sub = bt_mesh_subnet_find(beacon_cache_match, (void *)params->auth); + if (sub) { + /* We've seen this beacon before - just update the stats */ + net_beacon_register(sub); + 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; + } + + cache_add(params->auth, sub); + + net_beacon_recv(sub, params); + net_beacon_register(sub); +} + static void secure_beacon_recv(struct net_buf_simple *buf) { struct beacon_params params; - struct bt_mesh_subnet *sub; - uint8_t *data; if (buf->len < 21) { LOG_ERR("Too short secure beacon (len %u)", buf->len); return; } - sub = bt_mesh_subnet_find(beacon_cache_match, buf->data); - if (sub) { - /* We've seen this beacon before - just update the stats */ - goto update_stats; - } - - /* So we can add to the cache if auth matches */ - data = 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; - LOG_DBG("flags 0x%02x id %s iv_index 0x%08x", params.flags, bt_hex(params.net_id, 8), - params.iv_index); + net_beacon_resolve(¶ms, secure_beacon_authenticate); +} - sub = bt_mesh_subnet_find(subnet_by_id, ¶ms); - if (!sub) { - LOG_DBG("No subnet that matched beacon"); +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; } - if (sub->kr_phase == BT_MESH_KR_PHASE_2 && !params.new_key) { - LOG_WRN("Ignoring Phase 2 KR Update secured using old key"); - return; - } + params.private.random = net_buf_simple_pull_mem(buf, 13); + params.private.data = net_buf_simple_pull_mem(buf, 5); + params.auth = buf->data; - cache_add(data, 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) && - sub->net_idx != BT_MESH_KEY_PRIMARY) { - LOG_WRN("Ignoring secure beacon on non-primary subnet"); - goto update_stats; - } - - LOG_DBG("net_idx 0x%04x iv_index 0x%08x, current iv_index 0x%08x", 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(params.flags))) { - bt_mesh_beacon_ivu_initiator(false); - } - - bt_mesh_net_iv_update(params.iv_index, BT_MESH_IV_UPDATE(params.flags)); - -update_stats: - if (bt_mesh_beacon_enabled() && - sub->beacons_cur < 0xff) { - sub->beacons_cur++; - sub->beacon_recv = k_uptime_get_32(); - } + net_beacon_resolve(¶ms, priv_beacon_decrypt); } void bt_mesh_beacon_recv(struct net_buf_simple *buf) @@ -405,6 +584,9 @@ void bt_mesh_beacon_recv(struct net_buf_simple *buf) 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; @@ -415,7 +597,6 @@ 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)]; @@ -423,11 +604,13 @@ void bt_mesh_beacon_update(struct bt_mesh_subnet *sub) SUBNET_KEY_TX_IDX(sub) ? "new" : "current"); LOG_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) { - LOG_ERR("Failed updating net beacon for 0x%03x", sub->net_idx); - } +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + /* Invalidate private beacon to force regeneration: */ + sub->priv_beacon.idx = priv_random.idx - 1; +#endif + + bt_mesh_beacon_auth(keys->beacon, flags, keys->net_id, bt_mesh.iv_index, + sub->auth); } static void subnet_evt(struct bt_mesh_subnet *sub, enum bt_mesh_key_evt evt) @@ -444,6 +627,10 @@ BT_MESH_SUBNET_CB_DEFINE(beacon) = { 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) @@ -481,8 +668,12 @@ void bt_mesh_beacon_enable(void) void bt_mesh_beacon_disable(void) { - if (!atomic_test_bit(bt_mesh.flags, BT_MESH_IVU_INITIATOR)) { - /* If this fails, we'll do an early exit in the work handler. */ - (void)k_work_cancel_delayable(&beacon_timer); - } + /* 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); } diff --git a/subsys/bluetooth/mesh/beacon.h b/subsys/bluetooth/mesh/beacon.h index 6a3e87d7a1a..84a01eed0cb 100644 --- a/subsys/bluetooth/mesh/beacon.h +++ b/subsys/bluetooth/mesh/beacon.h @@ -11,8 +11,9 @@ void bt_mesh_beacon_ivu_initiator(bool enable); void bt_mesh_beacon_recv(struct net_buf_simple *buf); -void bt_mesh_beacon_create(struct bt_mesh_subnet *sub, - struct net_buf_simple *buf); +int 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); +void bt_mesh_beacon_priv_random_get(uint8_t *random, size_t size); diff --git a/subsys/bluetooth/mesh/cfg.c b/subsys/bluetooth/mesh/cfg.c index 28997513ffd..da3094f1c34 100644 --- a/subsys/bluetooth/mesh/cfg.c +++ b/subsys/bluetooth/mesh/cfg.c @@ -14,8 +14,8 @@ #include "settings.h" #include "heartbeat.h" #include "friend.h" -#include "cfg.h" #include "adv.h" +#include "cfg.h" #define LOG_LEVEL CONFIG_BT_MESH_CFG_LOG_LEVEL #include @@ -30,6 +30,10 @@ struct cfg_val { uint8_t gatt_proxy; uint8_t frnd; uint8_t default_ttl; +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + uint8_t priv_beacon; + uint8_t priv_beacon_int; +#endif }; void bt_mesh_beacon_set(bool beacon) @@ -82,6 +86,58 @@ static enum bt_mesh_feat_state feature_get(int feature_flag) BT_MESH_FEATURE_DISABLED; } +int bt_mesh_priv_beacon_set(enum bt_mesh_feat_state priv_beacon) +{ + int err; + + if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return -ENOTSUP; + } + + err = feature_set(BT_MESH_PRIV_BEACON, priv_beacon); + if (err) { + return err; + } + + if (priv_beacon == BT_MESH_FEATURE_ENABLED) { + bt_mesh_beacon_enable(); + } else if (bt_mesh_beacon_enabled()) { + bt_mesh_beacon_disable(); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); + } + + return 0; +} + +enum bt_mesh_feat_state bt_mesh_priv_beacon_get(void) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return BT_MESH_FEATURE_NOT_SUPPORTED; + } + + return feature_get(BT_MESH_PRIV_BEACON); +} + +void bt_mesh_priv_beacon_update_interval_set(uint8_t interval) +{ +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + bt_mesh.priv_beacon_int = interval; +#endif +} + +uint8_t bt_mesh_priv_beacon_update_interval_get(void) +{ +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + return bt_mesh.priv_beacon_int; +#else + return 0; +#endif +} + static bool node_id_is_running(struct bt_mesh_subnet *sub, void *cb_data) { return sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING; @@ -125,6 +181,42 @@ enum bt_mesh_feat_state bt_mesh_gatt_proxy_get(void) return feature_get(BT_MESH_GATT_PROXY); } +int bt_mesh_priv_gatt_proxy_set(enum bt_mesh_feat_state priv_gatt_proxy) +{ + int err; + + if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return BT_MESH_FEATURE_NOT_SUPPORTED; + } + + err = feature_set(BT_MESH_PRIV_GATT_PROXY, priv_gatt_proxy); + if (err) { + return err; + } + + if (priv_gatt_proxy == BT_MESH_FEATURE_ENABLED) { + /* Re-generate proxy beacon */ + bt_mesh_adv_gatt_update(); + } + + if (IS_ENABLED(CONFIG_BT_SETTINGS) && + atomic_test_bit(bt_mesh.flags, BT_MESH_VALID)) { + bt_mesh_settings_store_schedule(BT_MESH_SETTINGS_CFG_PENDING); + } + + return 0; +} + +enum bt_mesh_feat_state bt_mesh_priv_gatt_proxy_get(void) +{ + if (!IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) || !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return BT_MESH_FEATURE_NOT_SUPPORTED; + } + + return feature_get(BT_MESH_PRIV_GATT_PROXY); +} + + int bt_mesh_default_ttl_set(uint8_t default_ttl) { if (default_ttl == 1 || default_ttl > BT_MESH_TTL_MAX) { @@ -349,6 +441,11 @@ static void store_pending_cfg(void) val.gatt_proxy = bt_mesh_gatt_proxy_get(); val.frnd = bt_mesh_friend_get(); val.default_ttl = bt_mesh_default_ttl_get(); +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + val.priv_beacon = bt_mesh_priv_beacon_get(); + val.priv_beacon_int = bt_mesh_priv_beacon_update_interval_get(); +#endif + err = settings_save_one("bt/mesh/Cfg", &val, sizeof(val)); if (err) { diff --git a/subsys/bluetooth/mesh/cfg_srv.c b/subsys/bluetooth/mesh/cfg_srv.c index 275839b2c0e..b00145d79d3 100644 --- a/subsys/bluetooth/mesh/cfg_srv.c +++ b/subsys/bluetooth/mesh/cfg_srv.c @@ -546,6 +546,13 @@ static int gatt_proxy_set(struct bt_mesh_model *model, (void)bt_mesh_gatt_proxy_set(buf->data[0]); + /** 4.2.46.1: If the value of the Node Identity state of the node for any subnet is 0x01, + * then the value of the Private Node Identity state shall be Disable (0x00). + */ + if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) && buf->data[0]) { + (void)bt_mesh_priv_gatt_proxy_set(BT_MESH_FEATURE_DISABLED); + } + return send_gatt_proxy_status(model, ctx); } diff --git a/subsys/bluetooth/mesh/crypto.c b/subsys/bluetooth/mesh/crypto.c index b580b256206..01683bca233 100644 --- a/subsys/bluetooth/mesh/crypto.c +++ b/subsys/bluetooth/mesh/crypto.c @@ -702,3 +702,126 @@ int bt_mesh_dhkey_gen(const uint8_t *pub_key, const uint8_t *priv_key, uint8_t * return 0; } + +static int private_beacon_obf(const uint8_t pbk[16], const uint8_t data[5], + const uint8_t random[13], uint8_t out[5]) +{ + uint8_t salt[16]; + int i, err; + + /* C1 = 0x01 | random | 0x0001 */ + salt[0] = 0x01; + memcpy(&salt[1], random, 13); + sys_put_be16(0x0001, &salt[14]); + + /* ObfData = e(pbk, C1) ^ (flags | iv_index) */ + err = bt_encrypt_be(pbk, salt, salt); + if (err) { + return err; + } + + for (i = 0; i < 5; i++) { + out[i] = data[i] ^ salt[i]; + } + + + return 0; +} + +static int private_beacon_auth(const uint8_t pbk[16], + const uint8_t beacon_data[5], + const uint8_t random[13], uint8_t auth[8]) +{ + uint8_t salt[16], tmp[16]; + int i, err; + + /* B0 = 0x19 | random | 0x0005 */ + salt[0] = 0x19; + memcpy(&salt[1], random, 13); + sys_put_be16(0x0005, &salt[14]); + + /* T0 = e(PBK, b0) */ + err = bt_encrypt_be(pbk, salt, tmp); + if (err) { + return err; + } + + /* P = flags | iv_index | 000 */ + /* T1 = e(PBK, P ^ T0) */ + for (i = 0; i < 5; i++) { + tmp[i] ^= beacon_data[i]; + } + + err = bt_encrypt_be(pbk, tmp, tmp); + if (err) { + return err; + } + + /* C0 = 0x01 | random | 0x0000 */ + salt[0] = 0x01; + sys_put_be16(0x0000, &salt[14]); + + /* T2 = T1 ^ e(PBK, C0) */ + memcpy(auth, tmp, 8); + + err = bt_encrypt_be(pbk, salt, tmp); + if (err) { + return err; + } + + /* Auth = T2[0..7] */ + for (i = 0; i < 8; i++) { + auth[i] ^= tmp[i]; + } + + return 0; +} + +int bt_mesh_beacon_decrypt(const uint8_t pbk[16], const uint8_t random[13], + const uint8_t data[5], + const uint8_t expected_auth[8], uint8_t out[5]) +{ + uint8_t auth[8]; + int err; + + LOG_DBG(""); + + err = private_beacon_obf(pbk, data, random, out); + if (err) { + return err; + } + + err = private_beacon_auth(pbk, out, random, auth); + if (err) { + return err; + } + + LOG_DBG("0x%02x, 0x%08x", out[0], sys_get_be32(&out[1])); + + if (memcmp(auth, expected_auth, 8)) { + LOG_DBG("Invalid auth: %s expected %s", bt_hex(auth, 8), + bt_hex(expected_auth, 8)); + return -EBADMSG; + } + + return 0; +} + +int bt_mesh_beacon_encrypt(const uint8_t pbk[16], uint8_t flags, + uint32_t iv_index, const uint8_t random[13], + uint8_t data[5], uint8_t auth[8]) +{ + int err; + + LOG_WRN("Enc beacon: 0x%02x, 0x%08x", flags, iv_index); + + data[0] = flags; + sys_put_be32(iv_index, &data[1]); + + err = private_beacon_auth(pbk, data, random, auth); + if (err) { + return err; + } + + return private_beacon_obf(pbk, data, random, data); +} diff --git a/subsys/bluetooth/mesh/crypto.h b/subsys/bluetooth/mesh/crypto.h index 0886fe8bc18..72221d99eb0 100644 --- a/subsys/bluetooth/mesh/crypto.h +++ b/subsys/bluetooth/mesh/crypto.h @@ -56,6 +56,12 @@ static inline int bt_mesh_beacon_key(const uint8_t net_key[16], return bt_mesh_id128(net_key, "nkbk", beacon_key); } +static inline int bt_mesh_private_beacon_key(const uint8_t net_key[16], + uint8_t private_beacon_key[16]) +{ + return bt_mesh_id128(net_key, "nkpk", private_beacon_key); +} + int bt_mesh_beacon_auth(const uint8_t beacon_key[16], uint8_t flags, const uint8_t net_id[8], uint32_t iv_index, uint8_t auth[8]); @@ -150,3 +156,10 @@ int bt_mesh_prov_encrypt(const uint8_t key[16], uint8_t nonce[13], const uint8_t data[25], uint8_t out[25 + 8]); int bt_mesh_dhkey_gen(const uint8_t *pub_key, const uint8_t *priv_key, uint8_t *dhkey); + +int bt_mesh_beacon_decrypt(const uint8_t pbk[16], const uint8_t random[13], + const uint8_t data[5], const uint8_t expected_auth[8], + uint8_t out[5]); + +int bt_mesh_beacon_encrypt(const uint8_t pbk[16], uint8_t flags, uint32_t iv_index, + const uint8_t random[13], uint8_t data[5], uint8_t auth[8]); diff --git a/subsys/bluetooth/mesh/foundation.h b/subsys/bluetooth/mesh/foundation.h index 817a4166200..0be665c4d5c 100644 --- a/subsys/bluetooth/mesh/foundation.h +++ b/subsys/bluetooth/mesh/foundation.h @@ -102,6 +102,16 @@ #define OP_MODELS_METADATA_GET BT_MESH_MODEL_OP_2(0x80, 0x76) #define OP_MODELS_METADATA_STATUS BT_MESH_MODEL_OP_2(0x80, 0x77) +#define OP_PRIV_BEACON_GET BT_MESH_MODEL_OP_2(0x80, 0x60) +#define OP_PRIV_BEACON_SET BT_MESH_MODEL_OP_2(0x80, 0x61) +#define OP_PRIV_BEACON_STATUS BT_MESH_MODEL_OP_2(0x80, 0x62) +#define OP_PRIV_GATT_PROXY_GET BT_MESH_MODEL_OP_2(0x80, 0x63) +#define OP_PRIV_GATT_PROXY_SET BT_MESH_MODEL_OP_2(0x80, 0x64) +#define OP_PRIV_GATT_PROXY_STATUS BT_MESH_MODEL_OP_2(0x80, 0x65) +#define OP_PRIV_NODE_ID_GET BT_MESH_MODEL_OP_2(0x80, 0x66) +#define OP_PRIV_NODE_ID_SET BT_MESH_MODEL_OP_2(0x80, 0x67) +#define OP_PRIV_NODE_ID_STATUS BT_MESH_MODEL_OP_2(0x80, 0x68) + #define STATUS_SUCCESS 0x00 #define STATUS_INVALID_ADDRESS 0x01 #define STATUS_INVALID_MODEL 0x02 diff --git a/subsys/bluetooth/mesh/net.c b/subsys/bluetooth/mesh/net.c index 3c3ee0c9345..e2336f2e6e6 100644 --- a/subsys/bluetooth/mesh/net.c +++ b/subsys/bluetooth/mesh/net.c @@ -96,6 +96,10 @@ struct bt_mesh_net bt_mesh = { .sar_tx = BT_MESH_SAR_TX_INIT, .sar_rx = BT_MESH_SAR_RX_INIT, #endif + +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + .priv_beacon_int = 0x3c, +#endif }; /* Mesh Profile Specification 3.10.6 @@ -355,7 +359,8 @@ do_update: bt_mesh_subnet_foreach(bt_mesh_beacon_update); if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && - bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) { + (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || + bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED)) { bt_mesh_proxy_beacon_send(NULL); } @@ -673,7 +678,8 @@ static bool relay_to_adv(enum bt_mesh_net_if net_if) case BT_MESH_NET_IF_ADV: return (bt_mesh_relay_get() == BT_MESH_RELAY_ENABLED); case BT_MESH_NET_IF_PROXY: - return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); + return (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED) || + (bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED); default: return false; } @@ -693,7 +699,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, if (rx->net_if == BT_MESH_NET_IF_ADV && !rx->friend_cred && bt_mesh_relay_get() != BT_MESH_RELAY_ENABLED && - bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED) { + bt_mesh_gatt_proxy_get() != BT_MESH_GATT_PROXY_ENABLED && + bt_mesh_priv_gatt_proxy_get() != BT_MESH_PRIV_GATT_PROXY_ENABLED) { return; } @@ -747,7 +754,8 @@ static void bt_mesh_net_relay(struct net_buf_simple *sbuf, */ if (IS_ENABLED(CONFIG_BT_MESH_GATT_PROXY) && (rx->friend_cred || - bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) { + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || + bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_ENABLED)) { bt_mesh_proxy_relay(buf, rx->ctx.recv_dst); } @@ -858,6 +866,7 @@ void bt_mesh_net_recv(struct net_buf_simple *data, int8_t rssi, bt_mesh_proxy_addr_add(data, rx.ctx.addr); if (bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_DISABLED && + bt_mesh_priv_gatt_proxy_get() == BT_MESH_PRIV_GATT_PROXY_DISABLED && !rx.local_match) { LOG_INF("Proxy is disabled; ignoring message"); return; diff --git a/subsys/bluetooth/mesh/net.h b/subsys/bluetooth/mesh/net.h index 0b83d6664ea..2d18afbdff1 100644 --- a/subsys/bluetooth/mesh/net.h +++ b/subsys/bluetooth/mesh/net.h @@ -5,6 +5,7 @@ */ #include "subnet.h" +#include #define BT_MESH_IV_UPDATE(flags) ((flags >> 1) & 0x01) #define BT_MESH_KEY_REFRESH(flags) (flags & 0x01) @@ -185,6 +186,8 @@ enum { BT_MESH_BEACON, BT_MESH_GATT_PROXY, BT_MESH_FRIEND, + BT_MESH_PRIV_BEACON, + BT_MESH_PRIV_GATT_PROXY, /* Don't touch - intentionally last */ BT_MESH_FLAG_COUNT, @@ -216,6 +219,10 @@ struct bt_mesh_net { uint8_t relay_xmit; uint8_t default_ttl; +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + uint8_t priv_beacon_int; +#endif + /* Timer to track duration in current IV Update state */ struct k_work_delayable ivu_timer; diff --git a/subsys/bluetooth/mesh/priv_beacon_cli.c b/subsys/bluetooth/mesh/priv_beacon_cli.c new file mode 100644 index 00000000000..c02c9dd00f6 --- /dev/null +++ b/subsys/bluetooth/mesh/priv_beacon_cli.c @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "net.h" +#include "foundation.h" +#include "access.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_priv_beacon_cli); + +static struct bt_mesh_priv_beacon_cli *cli; + +static int handle_beacon_status(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_priv_beacon *rsp; + uint8_t beacon, rand_int; + + beacon = net_buf_simple_pull_u8(buf); + rand_int = net_buf_simple_pull_u8(buf); + + if (beacon != BT_MESH_BEACON_DISABLED && + beacon != BT_MESH_BEACON_ENABLED) { + LOG_WRN("Invalid beacon value 0x%02x", beacon); + return -EINVAL; + } + + LOG_DBG("0x%02x (%u s)", beacon, 10U * rand_int); + + if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, ctx->addr, + (void **)&rsp)) { + LOG_WRN("Unexpected beacon status from 0x%04x", ctx->addr); + return -EINVAL; + } + + rsp->enabled = beacon; + rsp->rand_interval = rand_int; + bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx); + + return 0; +} + +static int handle_gatt_proxy_status(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + uint8_t *rsp; + uint8_t proxy; + + proxy = net_buf_simple_pull_u8(buf); + + if (proxy != BT_MESH_GATT_PROXY_DISABLED && + proxy != BT_MESH_GATT_PROXY_ENABLED && + proxy != BT_MESH_GATT_PROXY_NOT_SUPPORTED) { + LOG_WRN("Invalid GATT proxy value 0x%02x", proxy); + return -EINVAL; + } + + if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, ctx->addr, + (void **)&rsp)) { + LOG_WRN("Unexpected proxy status from 0x%04x", ctx->addr); + return -EINVAL; + } + + *rsp = proxy; + bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx); + + return 0; +} + +static int handle_node_id_status(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + struct bt_mesh_priv_node_id *rsp; + uint8_t status, node_id; + uint16_t net_idx; + + status = net_buf_simple_pull_u8(buf); + net_idx = net_buf_simple_pull_le16(buf); + node_id = net_buf_simple_pull_u8(buf); + + if (node_id != BT_MESH_NODE_IDENTITY_STOPPED && + node_id != BT_MESH_NODE_IDENTITY_RUNNING && + node_id != BT_MESH_NODE_IDENTITY_NOT_SUPPORTED) { + LOG_WRN("Invalid node ID value 0x%02x", node_id); + return -EINVAL; + } + + + if (!bt_mesh_msg_ack_ctx_match(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, ctx->addr, + (void **)&rsp)) { + LOG_WRN("Unexpected node ID status from 0x%04x", ctx->addr); + return -EINVAL; + } + + rsp->status = status; + rsp->state = node_id; + bt_mesh_msg_ack_ctx_rx(&cli->ack_ctx); + + return 0; +} + +const struct bt_mesh_model_op bt_mesh_priv_beacon_cli_op[] = { + { OP_PRIV_BEACON_STATUS, BT_MESH_LEN_EXACT(2), handle_beacon_status }, + { OP_PRIV_GATT_PROXY_STATUS, BT_MESH_LEN_EXACT(1), handle_gatt_proxy_status }, + { OP_PRIV_NODE_ID_STATUS, BT_MESH_LEN_EXACT(4), handle_node_id_status }, + BT_MESH_MODEL_OP_END, +}; + +static int priv_beacon_cli_init(struct bt_mesh_model *mod) +{ + if (!bt_mesh_model_in_primary(mod)) { + LOG_ERR("Private Beacon Client only allowed in primary element"); + return -EINVAL; + } + + cli = mod->user_data; + cli->mod = mod; + cli->timeout = 2 * MSEC_PER_SEC; + mod->keys[0] = BT_MESH_KEY_DEV_ANY; + mod->flags |= BT_MESH_MOD_DEVKEY_ONLY; + + bt_mesh_msg_ack_ctx_init(&cli->ack_ctx); + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_priv_beacon_cli_cb = { + .init = priv_beacon_cli_init, +}; + +static int send(struct bt_mesh_priv_beacon_cli *cli, uint16_t net_idx, + uint16_t addr, struct net_buf_simple *buf) +{ + struct bt_mesh_msg_ctx ctx = { + .net_idx = net_idx, + .app_idx = BT_MESH_KEY_DEV_REMOTE, + .addr = addr, + .send_ttl = BT_MESH_TTL_DEFAULT, + }; + + return bt_mesh_model_send(cli->mod, &ctx, buf, NULL, NULL); +} + +int bt_mesh_priv_beacon_cli_set(uint16_t net_idx, uint16_t addr, struct bt_mesh_priv_beacon *val) +{ + int err; + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, addr, val); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_SET, 2); + bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_SET); + + net_buf_simple_add_u8(&buf, val->enabled); + if (val->rand_interval) { + net_buf_simple_add_u8(&buf, val->rand_interval); + } + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} + +int bt_mesh_priv_beacon_cli_get(uint16_t net_idx, uint16_t addr, struct bt_mesh_priv_beacon *val) +{ + int err; + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_BEACON_STATUS, addr, val); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_GET, 0); + bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_GET); + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} + +int bt_mesh_priv_beacon_cli_gatt_proxy_set(uint16_t net_idx, uint16_t addr, uint8_t *val) +{ + int err; + + if (!val || (*val != BT_MESH_GATT_PROXY_DISABLED && + *val != BT_MESH_GATT_PROXY_ENABLED)) { + return -EINVAL; + } + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, addr, val); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_SET, 1); + bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_SET); + + net_buf_simple_add_u8(&buf, *val); + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} + +int bt_mesh_priv_beacon_cli_gatt_proxy_get(uint16_t net_idx, uint16_t addr, uint8_t *val) +{ + int err; + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_GATT_PROXY_STATUS, addr, val); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_GET, 0); + bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_GET); + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} + +int bt_mesh_priv_beacon_cli_node_id_set(uint16_t net_idx, uint16_t addr, + struct bt_mesh_priv_node_id *val) +{ + int err; + + if (!val || val->net_idx > 0xfff || + (val->state != BT_MESH_NODE_IDENTITY_STOPPED && + val->state != BT_MESH_NODE_IDENTITY_RUNNING)) { + return -EINVAL; + } + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, addr, val); + if (err) { + return err; + } + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_SET, 3); + bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_SET); + + net_buf_simple_add_le16(&buf, val->net_idx); + net_buf_simple_add_u8(&buf, val->state); + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} + +int bt_mesh_priv_beacon_cli_node_id_get(uint16_t net_idx, uint16_t addr, uint16_t key_net_idx, + struct bt_mesh_priv_node_id *val) +{ + int err; + + err = bt_mesh_msg_ack_ctx_prepare(&cli->ack_ctx, OP_PRIV_NODE_ID_STATUS, addr, val); + if (err) { + return err; + } + + val->net_idx = key_net_idx; + + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_GET, 2); + bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_GET); + + net_buf_simple_add_le16(&buf, key_net_idx); + + err = send(cli, net_idx, addr, &buf); + if (err) { + bt_mesh_msg_ack_ctx_clear(&cli->ack_ctx); + return err; + } + + return bt_mesh_msg_ack_ctx_wait(&cli->ack_ctx, K_MSEC(cli->timeout)); +} diff --git a/subsys/bluetooth/mesh/priv_beacon_srv.c b/subsys/bluetooth/mesh/priv_beacon_srv.c new file mode 100644 index 00000000000..a1478efff80 --- /dev/null +++ b/subsys/bluetooth/mesh/priv_beacon_srv.c @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include +#include "net.h" +#include "adv.h" +#include +#include "proxy.h" +#include "foundation.h" +#include "beacon.h" +#include "cfg.h" + +#define LOG_LEVEL CONFIG_BT_MESH_MODEL_LOG_LEVEL +#include +LOG_MODULE_REGISTER(bt_mesh_priv_beacon_srv); + +static int beacon_status_rsp(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_BEACON_STATUS, 2); + bt_mesh_model_msg_init(&buf, OP_PRIV_BEACON_STATUS); + + net_buf_simple_add_u8(&buf, bt_mesh_priv_beacon_get()); + net_buf_simple_add_u8(&buf, bt_mesh_priv_beacon_update_interval_get()); + + bt_mesh_model_send(mod, ctx, &buf, NULL, NULL); + + return 0; +} + +static int handle_beacon_get(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + LOG_DBG(""); + + beacon_status_rsp(mod, ctx); + + return 0; +} + +static int handle_beacon_set(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + uint8_t beacon; + + if (buf->len > 2U) { + return -EMSGSIZE; + } + + beacon = net_buf_simple_pull_u8(buf); + if (beacon != BT_MESH_BEACON_DISABLED && + beacon != BT_MESH_BEACON_ENABLED) { + LOG_WRN("Invalid beacon value %u", beacon); + return -EINVAL; + } + + if (buf->len == 1U) { + bt_mesh_priv_beacon_update_interval_set(net_buf_simple_pull_u8(buf)); + } + + (void)bt_mesh_priv_beacon_set(beacon); + beacon_status_rsp(mod, ctx); + + return 0; +} + +static void gatt_proxy_status_rsp(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_GATT_PROXY_STATUS, 1); + bt_mesh_model_msg_init(&buf, OP_PRIV_GATT_PROXY_STATUS); + + net_buf_simple_add_u8(&buf, bt_mesh_priv_gatt_proxy_get()); + + bt_mesh_model_send(mod, ctx, &buf, NULL, NULL); +} + +static int handle_gatt_proxy_get(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + LOG_DBG(""); + + gatt_proxy_status_rsp(mod, ctx); + + return 0; +} + +static int handle_gatt_proxy_set(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + uint8_t gatt_proxy; + + gatt_proxy = net_buf_simple_pull_u8(buf); + if (gatt_proxy != BT_MESH_GATT_PROXY_DISABLED && + gatt_proxy != BT_MESH_GATT_PROXY_ENABLED) { + LOG_WRN("Invalid GATT proxy value %u", gatt_proxy); + return -EINVAL; + } + + LOG_DBG("%u", gatt_proxy); + + bt_mesh_priv_gatt_proxy_set(gatt_proxy); + + gatt_proxy_status_rsp(mod, ctx); + + return 0; +} + +static void node_id_status_rsp(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, uint8_t status, + uint16_t net_idx, uint8_t node_id) +{ + BT_MESH_MODEL_BUF_DEFINE(buf, OP_PRIV_NODE_ID_STATUS, 4); + bt_mesh_model_msg_init(&buf, OP_PRIV_NODE_ID_STATUS); + + net_buf_simple_add_u8(&buf, status); + net_buf_simple_add_le16(&buf, net_idx); + net_buf_simple_add_u8(&buf, node_id); + + bt_mesh_model_send(mod, ctx, &buf, NULL, NULL); +} + +static int handle_node_id_get(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + uint8_t node_id, status; + uint16_t net_idx; + + net_idx = net_buf_simple_pull_le16(buf) & 0xfff; + + status = bt_mesh_subnet_priv_node_id_get(net_idx, (enum bt_mesh_feat_state *)&node_id); + node_id_status_rsp(mod, ctx, status, net_idx, node_id); + + return 0; +} + +static int handle_node_id_set(struct bt_mesh_model *mod, + struct bt_mesh_msg_ctx *ctx, + struct net_buf_simple *buf) +{ + uint8_t node_id, status; + uint16_t net_idx; + + net_idx = net_buf_simple_pull_le16(buf) & 0xfff; + node_id = net_buf_simple_pull_u8(buf); + if (node_id != BT_MESH_NODE_IDENTITY_RUNNING && + node_id != BT_MESH_NODE_IDENTITY_STOPPED) { + LOG_ERR("Invalid node ID value 0x%02x", node_id); + return -EINVAL; + } + + status = bt_mesh_subnet_priv_node_id_set(net_idx, node_id); + node_id_status_rsp(mod, ctx, status, net_idx, node_id); + + return 0; +} + +const struct bt_mesh_model_op bt_mesh_priv_beacon_srv_op[] = { + { OP_PRIV_BEACON_GET, BT_MESH_LEN_EXACT(0), handle_beacon_get }, + { OP_PRIV_BEACON_SET, BT_MESH_LEN_MIN(1), handle_beacon_set }, + { OP_PRIV_GATT_PROXY_GET, BT_MESH_LEN_EXACT(0), handle_gatt_proxy_get }, + { OP_PRIV_GATT_PROXY_SET, BT_MESH_LEN_EXACT(1), handle_gatt_proxy_set }, + { OP_PRIV_NODE_ID_GET, BT_MESH_LEN_EXACT(2), handle_node_id_get }, + { OP_PRIV_NODE_ID_SET, BT_MESH_LEN_EXACT(3), handle_node_id_set }, + BT_MESH_MODEL_OP_END +}; + +static int priv_beacon_srv_init(struct bt_mesh_model *mod) +{ + if (!bt_mesh_model_in_primary(mod)) { + LOG_ERR("Priv beacon server not in primary element"); + return -EINVAL; + } + + mod->keys[0] = BT_MESH_KEY_DEV_LOCAL; + + return 0; +} + +const struct bt_mesh_model_cb bt_mesh_priv_beacon_srv_cb = { + .init = priv_beacon_srv_init, +}; diff --git a/subsys/bluetooth/mesh/proxy.h b/subsys/bluetooth/mesh/proxy.h index 1ae8ac4efe7..7f06be8f13f 100644 --- a/subsys/bluetooth/mesh/proxy.h +++ b/subsys/bluetooth/mesh/proxy.h @@ -18,8 +18,10 @@ .interval_min = BT_GAP_ADV_FAST_INT_MIN_2, \ .interval_max = BT_GAP_ADV_FAST_INT_MAX_2 -#define BT_MESH_ID_TYPE_NET 0x00 -#define BT_MESH_ID_TYPE_NODE 0x01 +#define BT_MESH_ID_TYPE_NET 0x00 +#define BT_MESH_ID_TYPE_NODE 0x01 +#define BT_MESH_ID_TYPE_PRIV_NET 0x02 +#define BT_MESH_ID_TYPE_PRIV_NODE 0x03 int bt_mesh_proxy_gatt_enable(void); int bt_mesh_proxy_gatt_disable(void); @@ -29,7 +31,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub); int bt_mesh_proxy_adv_start(void); -void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub); +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub, bool private); void bt_mesh_proxy_identity_stop(struct bt_mesh_subnet *sub); bool bt_mesh_proxy_relay(struct net_buf *buf, uint16_t dst); diff --git a/subsys/bluetooth/mesh/proxy_srv.c b/subsys/bluetooth/mesh/proxy_srv.c index 507e68d7412..7a17da770ea 100644 --- a/subsys/bluetooth/mesh/proxy_srv.c +++ b/subsys/bluetooth/mesh/proxy_srv.c @@ -13,6 +13,7 @@ #include #include #include +#include #include @@ -35,6 +36,12 @@ #include LOG_MODULE_REGISTER(bt_mesh_gatt); +/* Interval to update random value in (10 minutes). + * + * Defined in the Bluetooth Mesh Specification v1.1, Section 7.2.2.2.4. + */ +#define PROXY_RANDOM_UPDATE_INTERVAL (10 * 60 * MSEC_PER_SEC) + #if defined(CONFIG_BT_MESH_PROXY_USE_DEVICE_NAME) #define ADV_OPT_USE_NAME BT_LE_ADV_OPT_USE_NAME #else @@ -312,10 +319,15 @@ static void proxy_msg_recv(struct bt_mesh_proxy_role *role) static int beacon_send(struct bt_mesh_proxy_client *client, struct bt_mesh_subnet *sub) { - NET_BUF_SIMPLE_DEFINE(buf, 23); + int err; + + NET_BUF_SIMPLE_DEFINE(buf, 28); net_buf_simple_reserve(&buf, 1); - bt_mesh_beacon_create(sub, &buf); + err = bt_mesh_beacon_create(sub, &buf); + if (err) { + return err; + } return bt_mesh_proxy_msg_send(client->cli->conn, BT_MESH_PROXY_BEACON, &buf, NULL, NULL); @@ -354,7 +366,7 @@ void bt_mesh_proxy_beacon_send(struct bt_mesh_subnet *sub) } } -static void node_id_start(struct bt_mesh_subnet *sub) +static void identity_enabled(struct bt_mesh_subnet *sub) { sub->node_id = BT_MESH_NODE_IDENTITY_RUNNING; sub->node_id_start = k_uptime_get_32(); @@ -366,9 +378,31 @@ static void node_id_start(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) { - node_id_start(sub); +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + sub->priv_beacon.node_id = false; +#endif + + identity_enabled(sub); +} + +static void private_node_id_start(struct bt_mesh_subnet *sub) +{ +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + sub->priv_beacon.node_id = true; +#endif + + identity_enabled(sub); +} + +void bt_mesh_proxy_identity_start(struct bt_mesh_subnet *sub, bool private) +{ + if (private) { + private_node_id_start(sub); + } else { + node_id_start(sub); + } /* Prioritize the recently enabled subnet */ beacon_sub = sub; @@ -401,20 +435,39 @@ int bt_mesh_proxy_identity_enable(void) return 0; } -#define NODE_ID_LEN 19 +int bt_mesh_proxy_private_identity_enable(void) +{ + LOG_DBG(""); + + if (!IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return -ENOTSUP; + } + + if (!bt_mesh_is_provisioned()) { + return -EAGAIN; + } + + if (bt_mesh_subnet_foreach(private_node_id_start)) { + bt_mesh_adv_gatt_update(); + } + + return 0; +} + +#define ENC_ID_LEN 19 #define NET_ID_LEN 11 #define NODE_ID_TIMEOUT (CONFIG_BT_MESH_NODE_ID_TIMEOUT * MSEC_PER_SEC) -static uint8_t proxy_svc_data[NODE_ID_LEN] = { +static uint8_t proxy_svc_data[ENC_ID_LEN] = { BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL), }; -static const struct bt_data node_id_ad[] = { +static const struct bt_data enc_id_ad[] = { BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(BT_UUID_MESH_PROXY_VAL)), - BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NODE_ID_LEN), + BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, ENC_ID_LEN), }; static const struct bt_data net_id_ad[] = { @@ -424,46 +477,113 @@ static const struct bt_data net_id_ad[] = { BT_DATA(BT_DATA_SVC_DATA16, proxy_svc_data, NET_ID_LEN), }; -static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration) +static int randomize_bt_addr(void) { + /* TODO: There appears to be no way to force an RPA/NRPA refresh. */ + return 0; +} + +static int enc_id_adv(struct bt_mesh_subnet *sub, uint8_t type, + uint8_t hash[16], int32_t duration) +{ + struct bt_le_adv_param slow_adv_param = { + .options = ADV_OPT_PROXY, + ADV_SLOW_INT, + }; struct bt_le_adv_param fast_adv_param = { .options = ADV_OPT_PROXY, ADV_FAST_INT, }; - uint8_t tmp[16]; int err; - LOG_DBG(""); - - proxy_svc_data[2] = BT_MESH_ID_TYPE_NODE; - - err = bt_rand(proxy_svc_data + 11, 8); + err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, hash, hash); if (err) { return err; } - (void)memset(tmp, 0, 6); - memcpy(tmp + 6, proxy_svc_data + 11, 8); - sys_put_be16(bt_mesh_primary_addr(), tmp + 14); - - err = bt_encrypt_be(sub->keys[SUBNET_KEY_TX_IDX(sub)].identity, tmp, - tmp); + /* Section 7.2.2.2.4: The AdvA field shall be regenerated whenever the Random field is + * regenerated. + */ + err = randomize_bt_addr(); if (err) { + LOG_ERR("AdvA refresh failed: %d", err); return err; } - memcpy(proxy_svc_data + 3, tmp + 8, 8); + proxy_svc_data[2] = type; + memcpy(&proxy_svc_data[3], &hash[8], 8); - err = bt_mesh_adv_gatt_start(&fast_adv_param, duration, node_id_ad, - ARRAY_SIZE(node_id_ad), NULL, 0); + err = bt_mesh_adv_gatt_start( + type == BT_MESH_ID_TYPE_PRIV_NET ? &slow_adv_param : &fast_adv_param, + duration, enc_id_ad, ARRAY_SIZE(enc_id_ad), NULL, 0); if (err) { - LOG_WRN("Failed to advertise using Node ID (err %d)", err); + LOG_WRN("Failed to advertise using type 0x%02x (err %d)", type, err); return err; } return 0; } +static int node_id_adv(struct bt_mesh_subnet *sub, int32_t duration) +{ + uint8_t *random = &proxy_svc_data[11]; + uint8_t tmp[16]; + int err; + + LOG_DBG("0x%03x", sub->net_idx); + + err = bt_rand(random, 8); + if (err) { + return err; + } + + memset(&tmp[0], 0x00, 6); + memcpy(&tmp[6], random, 8); + sys_put_be16(bt_mesh_primary_addr(), &tmp[14]); + + return enc_id_adv(sub, BT_MESH_ID_TYPE_NODE, tmp, duration); +} + +static int priv_node_id_adv(struct bt_mesh_subnet *sub, int32_t duration) +{ + uint8_t *random = &proxy_svc_data[11]; + uint8_t tmp[16]; + int err; + + LOG_DBG("0x%03x", sub->net_idx); + + err = bt_rand(random, 8); + if (err) { + return err; + } + + memset(&tmp[0], 0x00, 5); + tmp[5] = 0x03; + memcpy(&tmp[6], random, 8); + sys_put_be16(bt_mesh_primary_addr(), &tmp[14]); + + return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NODE, tmp, duration); +} + +static int priv_net_id_adv(struct bt_mesh_subnet *sub, int32_t duration) +{ + uint8_t *random = &proxy_svc_data[11]; + uint8_t tmp[16]; + int err; + + LOG_DBG("0x%03x", sub->net_idx); + + err = bt_rand(random, 8); + if (err) { + return err; + } + + memcpy(&tmp[0], sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8); + memcpy(&tmp[8], random, 8); + + return enc_id_adv(sub, BT_MESH_ID_TYPE_PRIV_NET, tmp, duration); +} + static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration) { struct bt_le_adv_param slow_adv_param = { @@ -472,8 +592,6 @@ static int net_id_adv(struct bt_mesh_subnet *sub, int32_t duration) }; int err; - LOG_DBG(""); - proxy_svc_data[2] = BT_MESH_ID_TYPE_NET; LOG_DBG("Advertising with NetId %s", bt_hex(sub->keys[SUBNET_KEY_TX_IDX(sub)].net_id, 8)); @@ -497,7 +615,8 @@ static bool advertise_subnet(struct bt_mesh_subnet *sub) } return (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING || - bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); + bt_mesh_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED || + bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED); } static struct bt_mesh_subnet *next_sub(void) @@ -591,12 +710,20 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub) if (sub->node_id == BT_MESH_NODE_IDENTITY_RUNNING) { uint32_t active = k_uptime_get_32() - sub->node_id_start; + bool priv_node_id = false; if (active < NODE_ID_TIMEOUT) { remaining = MIN(remaining, NODE_ID_TIMEOUT - active); - LOG_DBG("Node ID active for %u ms, %d ms remaining", active, - remaining); - err = node_id_adv(sub, remaining); + LOG_DBG("Node ID active for %u ms, %d ms remaining", + active, remaining); +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + priv_node_id = sub->priv_beacon.node_id; +#endif + if (priv_node_id) { + err = priv_node_id_adv(sub, remaining); + } else { + err = node_id_adv(sub, remaining); + } planned = true; } else { bt_mesh_proxy_identity_stop(sub); @@ -608,10 +735,25 @@ static int gatt_proxy_advertise(struct bt_mesh_subnet *sub) * A node that does not support the Proxy feature or * has the GATT Proxy state disabled shall not advertise with Network ID. */ - if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED && - bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) { - err = net_id_adv(sub, remaining); - planned = true; + if (sub->node_id == BT_MESH_NODE_IDENTITY_STOPPED) { + if (IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS) && + (bt_mesh_priv_gatt_proxy_get() == BT_MESH_GATT_PROXY_ENABLED)) { + /* Bluetooth mesh specification v1.1, section 7.2.2.2.4: The Random + * field should be updated every 10 minutes. Limit advertising to + * 10 minutes to ensure regeneration of a new random value at least + * that often. + */ + if (remaining == SYS_FOREVER_MS || + remaining > PROXY_RANDOM_UPDATE_INTERVAL) { + remaining = PROXY_RANDOM_UPDATE_INTERVAL; + } + + err = priv_net_id_adv(sub, remaining); + planned = true; + } else if (bt_mesh_gatt_proxy_get() == BT_MESH_FEATURE_ENABLED) { + err = net_id_adv(sub, remaining); + planned = true; + } } beacon_sub = bt_mesh_subnet_next(sub); diff --git a/subsys/bluetooth/mesh/subnet.c b/subsys/bluetooth/mesh/subnet.c index f3b05ecbf82..a8358b3cbf4 100644 --- a/subsys/bluetooth/mesh/subnet.c +++ b/subsys/bluetooth/mesh/subnet.c @@ -328,6 +328,16 @@ static int net_keys_create(struct bt_mesh_subnet_keys *keys, LOG_DBG("BeaconKey %s", bt_hex(keys->beacon, 16)); +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + err = bt_mesh_private_beacon_key(key, keys->priv_beacon); + if (err) { + LOG_ERR("Unable to generate private beacon key"); + return err; + } + + LOG_DBG("PrivateBeaconKey %s", bt_hex(keys->priv_beacon, 16)); +#endif + keys->valid = 1U; return 0; @@ -533,7 +543,7 @@ uint8_t bt_mesh_subnet_node_id_set(uint16_t net_idx, } if (node_id) { - bt_mesh_proxy_identity_start(sub); + bt_mesh_proxy_identity_start(sub, false); } else { bt_mesh_proxy_identity_stop(sub); } @@ -559,6 +569,61 @@ uint8_t bt_mesh_subnet_node_id_get(uint16_t net_idx, return STATUS_SUCCESS; } + +uint8_t bt_mesh_subnet_priv_node_id_set(uint16_t net_idx, + enum bt_mesh_feat_state priv_node_id) +{ + struct bt_mesh_subnet *sub; + + if (priv_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) || + !IS_ENABLED(CONFIG_BT_MESH_PRIV_BEACONS)) { + return STATUS_FEAT_NOT_SUPP; + } + + if (priv_node_id) { + bt_mesh_proxy_identity_start(sub, true); + } else { + bt_mesh_proxy_identity_stop(sub); + } + + bt_mesh_adv_gatt_update(); + + return STATUS_SUCCESS; +} + +uint8_t bt_mesh_subnet_priv_node_id_get(uint16_t net_idx, + enum bt_mesh_feat_state *priv_node_id) +{ + struct bt_mesh_subnet *sub; + + sub = bt_mesh_subnet_get(net_idx); + if (!sub) { + *priv_node_id = 0x00; + return STATUS_INVALID_NETKEY; + } + +#if CONFIG_BT_MESH_GATT_PROXY && CONFIG_BT_MESH_PRIV_BEACONS + if (sub->node_id == BT_MESH_FEATURE_ENABLED && sub->priv_beacon.node_id) { + *priv_node_id = sub->node_id; + } else { + *priv_node_id = BT_MESH_FEATURE_DISABLED; + } +#else + *priv_node_id = BT_MESH_FEATURE_NOT_SUPPORTED; +#endif + + return STATUS_SUCCESS; +} + ssize_t bt_mesh_subnets_get(uint16_t net_idxs[], size_t max, off_t skip) { size_t count = 0; diff --git a/subsys/bluetooth/mesh/subnet.h b/subsys/bluetooth/mesh/subnet.h index 232bcb741ca..c08cee47d0a 100644 --- a/subsys/bluetooth/mesh/subnet.h +++ b/subsys/bluetooth/mesh/subnet.h @@ -44,7 +44,7 @@ struct bt_mesh_subnet { * currently ongoing window. */ - uint8_t beacon_cache[21]; /* Cached last authenticated beacon */ + uint8_t beacon_cache[8]; /* Cached last beacon auth value */ uint16_t net_idx; /* NetKeyIndex */ @@ -53,6 +53,13 @@ struct bt_mesh_subnet { uint8_t node_id; /* Node Identity State */ uint32_t node_id_start; /* Node Identity started timestamp */ +#if defined(CONFIG_BT_MESH_PRIV_BEACONS) + struct { + uint16_t idx; /* Private beacon random index */ + bool node_id; /* Private Node Identity enabled */ + uint8_t data[5]; /* Private Beacon data */ + } priv_beacon; +#endif uint8_t auth[8]; /* Beacon Authentication Value */ struct bt_mesh_subnet_keys { @@ -63,7 +70,8 @@ struct bt_mesh_subnet { #if defined(CONFIG_BT_MESH_GATT_PROXY) uint8_t identity[16]; /* IdentityKey */ #endif - uint8_t beacon[16]; /* BeaconKey */ + uint8_t beacon[16]; /* BeaconKey */ + uint8_t priv_beacon[16]; /* PrivateBeaconKey */ } keys[2]; }; @@ -183,6 +191,8 @@ uint8_t bt_mesh_net_flags(struct bt_mesh_subnet *sub); * @param kr_flag Key Refresh flag. * @param new_key Whether the Key Refresh event was received on the new key * set. + * + * @returns Whether the Key Refresh event caused a change. */ void bt_mesh_kr_update(struct bt_mesh_subnet *sub, bool kr_flag, bool new_key);