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