Bluetooth: Mesh: Ensure seqnum match in app/net

Re-encrypts single-segment application messages when the network seqnum
has changed, to avoid encrypting messages with different seqnums in
network and transport. This operation is only required for unsegmented
messages, as segmented messages don't need to use the same seqnum in
network.

Reinstates the special adv data for friend messages to store the app key
index.

Fixes #19265.

Signed-off-by: Trond Einar Snekvik <Trond.Einar.Snekvik@nordicsemi.no>
This commit is contained in:
Trond Einar Snekvik 2019-09-30 13:33:36 +02:00 committed by Johan Hedberg
commit a7cf776d52

View file

@ -33,6 +33,8 @@
#define FRIEND_BUF_COUNT ((CONFIG_BT_MESH_FRIEND_QUEUE_SIZE + 1) * \
CONFIG_BT_MESH_FRIEND_LPN_COUNT)
#define FRIEND_ADV(buf) CONTAINER_OF(BT_MESH_ADV(buf), struct friend_adv, adv)
/* PDUs from Friend to the LPN should only be transmitted once with the
* smallest possible interval (20ms).
*/
@ -53,11 +55,15 @@ struct friend_pdu_info {
NET_BUF_POOL_FIXED_DEFINE(friend_buf_pool, FRIEND_BUF_COUNT,
BT_MESH_ADV_DATA_SIZE, NULL);
static struct bt_mesh_adv adv_pool[FRIEND_BUF_COUNT];
static struct friend_adv {
struct bt_mesh_adv adv;
u16_t app_idx;
} adv_pool[FRIEND_BUF_COUNT];
static struct bt_mesh_adv *adv_alloc(int id)
{
return &adv_pool[id];
adv_pool[id].app_idx = BT_MESH_KEY_UNUSED;
return &adv_pool[id].adv;
}
static bool is_lpn_unicast(struct bt_mesh_friend *frnd, u16_t addr)
@ -314,13 +320,122 @@ static struct net_buf *create_friend_pdu(struct bt_mesh_friend *frnd,
return buf;
}
static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct net_buf *buf, bool master_cred)
struct unseg_app_sdu_meta {
struct bt_mesh_net_rx net;
const u8_t *key;
struct bt_mesh_subnet *subnet;
bool is_dev_key;
u8_t aid;
u8_t *ad;
};
static int unseg_app_sdu_unpack(struct bt_mesh_friend *frnd,
struct net_buf *buf,
struct unseg_app_sdu_meta *meta)
{
u16_t app_idx = FRIEND_ADV(buf)->app_idx;
int err;
meta->subnet = bt_mesh_subnet_get(frnd->net_idx);
meta->is_dev_key = (app_idx == BT_MESH_KEY_DEV);
bt_mesh_net_header_parse(&buf->b, &meta->net);
err = bt_mesh_app_key_get(meta->subnet, app_idx, &meta->key,
&meta->aid);
if (err) {
return err;
}
if (BT_MESH_ADDR_IS_VIRTUAL(meta->net.ctx.recv_dst)) {
meta->ad = bt_mesh_label_uuid_get(meta->net.ctx.recv_dst);
if (!meta->ad) {
return -ENOENT;
}
} else {
meta->ad = NULL;
}
return 0;
}
static int unseg_app_sdu_decrypt(struct bt_mesh_friend *frnd,
struct net_buf *buf,
const struct unseg_app_sdu_meta *meta)
{
struct net_buf_simple sdu = {
.len = buf->len - 14,
.data = &buf->data[10],
.__buf = &buf->data[10],
.size = buf->len - 10,
};
return bt_mesh_app_decrypt(meta->key, meta->is_dev_key, 0, &sdu, &sdu,
meta->ad, meta->net.ctx.addr,
meta->net.ctx.recv_dst, meta->net.seq,
BT_MESH_NET_IVI_TX);
}
static int unseg_app_sdu_encrypt(struct bt_mesh_friend *frnd,
struct net_buf *buf,
const struct unseg_app_sdu_meta *meta)
{
struct net_buf_simple sdu = {
.len = buf->len - 14,
.data = &buf->data[10],
.__buf = &buf->data[10],
.size = buf->len - 10,
};
return bt_mesh_app_encrypt(meta->key, meta->is_dev_key, 0, &sdu,
meta->ad, meta->net.ctx.addr,
meta->net.ctx.recv_dst, bt_mesh.seq,
BT_MESH_NET_IVI_TX);
}
static int unseg_app_sdu_prepare(struct bt_mesh_friend *frnd,
struct net_buf *buf)
{
struct unseg_app_sdu_meta meta;
int err;
if (FRIEND_ADV(buf)->app_idx == BT_MESH_KEY_UNUSED) {
return 0;
}
err = unseg_app_sdu_unpack(frnd, buf, &meta);
if (err) {
return err;
}
/* No need to reencrypt the message if the sequence number is
* unchanged.
*/
if (meta.net.seq == bt_mesh.seq) {
return 0;
}
err = unseg_app_sdu_decrypt(frnd, buf, &meta);
if (err) {
BT_WARN("Decryption failed! %d", err);
return err;
}
err = unseg_app_sdu_encrypt(frnd, buf, &meta);
if (err) {
BT_WARN("Re-encryption failed! %d", err);
}
return err;
}
static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct net_buf *buf,
bool master_cred)
{
struct bt_mesh_subnet *sub = bt_mesh_subnet_get(frnd->net_idx);
const u8_t *enc, *priv;
u32_t iv_index;
u16_t src;
u8_t nid;
int err;
if (master_cred) {
enc = sub->keys[sub->kr_flag].enc;
@ -336,12 +451,22 @@ static int encrypt_friend_pdu(struct bt_mesh_friend *frnd, struct net_buf *buf,
src = sys_get_be16(&buf->data[5]);
if (bt_mesh_elem_find(src)) {
/* Local messages were stored with a blank seqnum */
u32_t seq = bt_mesh_next_seq();
u32_t seq;
if (FRIEND_ADV(buf)->app_idx != BT_MESH_KEY_UNUSED) {
err = unseg_app_sdu_prepare(frnd, buf);
if (err) {
return err;
}
}
seq = bt_mesh_next_seq();
buf->data[2] = seq >> 16;
buf->data[3] = seq >> 8;
buf->data[4] = seq;
iv_index = BT_MESH_NET_IVI_TX;
FRIEND_ADV(buf)->app_idx = BT_MESH_KEY_UNUSED;
} else {
u8_t ivi = (buf->data[0] >> 7);
iv_index = (bt_mesh.iv_index - ((bt_mesh.iv_index & 1) != ivi));
@ -1205,7 +1330,9 @@ static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd,
info.ttl = tx->ctx->send_ttl;
info.ctl = (tx->ctx->app_idx == BT_MESH_KEY_UNUSED);
memset(info.seq, 0, sizeof(info.seq)); /* Will be allocated before encryption */
info.seq[0] = (bt_mesh.seq >> 16);
info.seq[1] = (bt_mesh.seq >> 8);
info.seq[2] = bt_mesh.seq;
info.iv_index = BT_MESH_NET_IVI_TX;
@ -1215,6 +1342,14 @@ static void friend_lpn_enqueue_tx(struct bt_mesh_friend *frnd,
return;
}
if (type == BT_MESH_FRIEND_PDU_SINGLE && !info.ctl) {
/* Unsegmented application packets may be reencrypted later,
* as they depend on the the sequence number being the same
* when encrypting in transport and network.
*/
FRIEND_ADV(buf)->app_idx = tx->ctx->app_idx;
}
enqueue_friend_pdu(frnd, type, info.src, seg_count, buf);
BT_DBG("Queued message for LPN 0x%04x", frnd->lpn);