// SPDX-License-Identifier: LGPL-2.1-or-later /* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2019 Intel Corporation. All rights reserved. * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include "mesh/mesh-defs.h" #include "mesh/util.h" #include "mesh/crypto.h" #include "mesh/mesh-io.h" #include "mesh/net.h" #include "mesh/net-keys.h" #define BEACON_INTERVAL_MIN 10 #define BEACON_INTERVAL_MAX 600 /* This allows daemon to skip decryption on recently seen beacons */ #define BEACON_CACHE_MAX 10 struct beacon_rx { uint8_t data[28]; uint32_t id; uint32_t ivi; bool kr; bool ivu; }; struct beacon_observe { struct l_timeout *timeout; uint32_t ts; uint16_t period; uint16_t seen; uint16_t expected; bool half_period; }; struct net_key { uint32_t id; struct l_timeout *mpb_to; uint8_t *mpb; uint8_t *snb; struct beacon_observe observe; uint32_t ivi; uint16_t ref_cnt; uint16_t mpb_enables; uint16_t snb_enables; uint8_t mpb_refresh; uint8_t friend_key; uint8_t nid; uint8_t flooding[16]; uint8_t enc_key[16]; uint8_t prv_key[16]; uint8_t snb_key[16]; uint8_t pvt_key[16]; uint8_t net_id[8]; bool kr; bool ivu; }; static struct l_queue *beacons; static struct l_queue *keys; static uint32_t last_flooding_id; /* To avoid re-decrypting same packet for multiple nodes, cache and check */ static uint8_t cache_pkt[29]; static uint8_t cache_plain[29]; static size_t cache_len; static size_t cache_plainlen; static uint32_t cache_id; static uint32_t cache_iv_index; static bool match_flooding(const void *a, const void *b) { const struct net_key *key = a; return (memcmp(key->flooding, b, sizeof(key->flooding)) == 0); } static bool match_id(const void *a, const void *b) { const struct net_key *key = a; uint32_t id = L_PTR_TO_UINT(b); return id == key->id; } static bool match_network(const void *a, const void *b) { const struct net_key *key = a; const uint8_t *net_id = b; return memcmp(key->net_id, net_id, sizeof(key->net_id)) == 0; } /* Key added from Provisioning, NetKey Add or NetKey update */ uint32_t net_key_add(const uint8_t flooding[16]) { struct net_key *key = l_queue_find(keys, match_flooding, flooding); uint8_t p[] = {0}; bool result; if (key) { key->ref_cnt++; return key->id; } if (!keys) keys = l_queue_new(); if (!beacons) beacons = l_queue_new(); key = l_new(struct net_key, 1); memcpy(key->flooding, flooding, 16); key->ref_cnt++; key->mpb_refresh = NET_MPB_REFRESH_DEFAULT; result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key, key->prv_key); if (!result) goto fail; result = mesh_crypto_k3(flooding, key->net_id); if (!result) goto fail; result = mesh_crypto_nkbk(flooding, key->snb_key); if (!result) goto fail; result = mesh_crypto_nkpk(flooding, key->pvt_key); if (!result) goto fail; key->id = ++last_flooding_id; l_queue_push_tail(keys, key); return key->id; fail: l_free(key); return 0; } uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd, uint16_t lp_cnt, uint16_t fn_cnt) { const struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(flooding_id)); struct net_key *frnd_key; uint8_t p[9] = {0x01}; bool result; if (!key || key->friend_key) return 0; frnd_key = l_new(struct net_key, 1); l_put_be16(lpn, p + 1); l_put_be16(frnd, p + 3); l_put_be16(lp_cnt, p + 5); l_put_be16(fn_cnt, p + 7); result = mesh_crypto_k2(key->flooding, p, sizeof(p), &frnd_key->nid, frnd_key->enc_key, frnd_key->prv_key); if (!result) { l_free(frnd_key); return 0; } frnd_key->friend_key = true; frnd_key->ref_cnt++; frnd_key->id = ++last_flooding_id; l_queue_push_head(keys, frnd_key); return frnd_key->id; } void net_key_unref(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key && key->ref_cnt) { if (--key->ref_cnt == 0) { l_timeout_remove(key->observe.timeout); l_queue_remove(keys, key); l_free(key); } } } bool net_key_confirm(uint32_t id, const uint8_t flooding[16]) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) return !memcmp(key->flooding, flooding, sizeof(key->flooding)); return false; } bool net_key_retrieve(uint32_t id, uint8_t *flooding) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) { memcpy(flooding, key->flooding, sizeof(key->flooding)); return true; } return false; } static void decrypt_net_pkt(void *a, void *b) { const struct net_key *key = a; bool result; if (cache_id || !key->ref_cnt || (cache_pkt[0] & 0x7f) != key->nid) return; result = mesh_crypto_packet_decode(cache_pkt, cache_len, false, cache_plain, cache_iv_index, key->enc_key, key->prv_key); if (result) { cache_id = key->id; if (cache_plain[1] & 0x80) cache_plainlen = cache_len - 8; else cache_plainlen = cache_len - 4; } } uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len, uint8_t **plain, size_t *plain_len) { /* If we already successfully decrypted this packet, use cached data */ if (cache_id && cache_len == len && !memcmp(pkt, cache_pkt, len)) { /* IV Index must match what was used to decrypt */ if (cache_iv_index != iv_index) return 0; goto done; } cache_id = 0; memcpy(cache_pkt, pkt, len); cache_len = len; cache_iv_index = iv_index; /* Try all network keys known to us */ l_queue_foreach(keys, decrypt_net_pkt, NULL); done: if (cache_id) { *plain = cache_plain; *plain_len = cache_plainlen; } return cache_id; } bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool result; if (!key) return false; result = mesh_crypto_packet_encode(pkt, len, iv_index, key->enc_key, key->prv_key); if (!result) return false; result = mesh_crypto_packet_label(pkt, len, iv_index, key->nid); return result; } uint32_t net_key_network_id(const uint8_t net_id[8]) { struct net_key *key = l_queue_find(keys, match_network, net_id); if (!key) return 0; return key->id; } struct auth_check { const uint8_t *data; uint32_t id; uint32_t ivi; bool ivu; bool kr; }; static void check_auth(void *a, void *b) { struct net_key *key = a; struct auth_check *auth = b; uint8_t out[5]; /* Stop checking if already found */ if (auth->id) return; if (mesh_crypto_aes_ccm_decrypt(auth->data + 1, key->pvt_key, NULL, 0, auth->data + 14, 13, out, NULL, 8)) { auth->id = key->id; auth->ivi = l_get_be32(out + 1); auth->ivu = !!(out[0] & 0x02); auth->kr = !!(out[0] & 0x01); } } static uint32_t private_beacon_check(const void *beacon, uint32_t *ivi, bool *ivu, bool *kr) { struct auth_check auth = { .data = beacon, .id = 0, }; auth.id = 0; l_queue_foreach(keys, check_auth, &auth); if (auth.id) { *ivi = auth.ivi; *ivu = auth.ivu; *kr = auth.kr; } return auth.id; } bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu, uint64_t cmac) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); uint64_t cmac_check; if (!key) return false; /* Any behavioral changes must pass CMAC test */ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, iv_index, kr, ivu, &cmac_check)) { l_error("mesh_crypto_beacon_cmac failed"); return false; } if (cmac != cmac_check) { l_error("cmac compare failed 0x%16" PRIx64 " != 0x%16" PRIx64, cmac, cmac_check); return false; } return true; } static bool mpb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) { uint8_t b_data[5 + 8]; uint8_t random[13]; if (!key) return false; b_data[0] = 0; l_put_be32(ivi, b_data + 1); if (kr) b_data[0] |= KEY_REFRESH; if (ivu) b_data[0] |= IV_INDEX_UPDATE; l_getrandom(random, sizeof(random)); if (!mesh_crypto_aes_ccm_encrypt(random, key->pvt_key, NULL, 0, b_data, 5, b_data, NULL, 8)) return false; key->mpb[0] = MESH_AD_TYPE_BEACON; key->mpb[1] = BEACON_TYPE_MPB; memcpy(key->mpb + 2, random, 13); memcpy(key->mpb + 15, b_data, 13); return true; } static bool snb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu) { uint64_t cmac; if (!key) return false; /* Any behavioral changes must pass CMAC test */ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, ivi, kr, ivu, &cmac)) { l_error("mesh_crypto_beacon_cmac failed"); return false; } key->snb[0] = MESH_AD_TYPE_BEACON; key->snb[1] = BEACON_TYPE_SNB; key->snb[2] = 0; if (kr) key->snb[2] |= KEY_REFRESH; if (ivu) key->snb[2] |= IV_INDEX_UPDATE; memcpy(key->snb + 3, key->net_id, 8); l_put_be32(ivi, key->snb + 11); l_put_be64(cmac, key->snb + 15); return true; } static bool match_beacon(const void *a, const void *b) { const struct beacon_rx *cached = a; const uint8_t *incoming = b; if (incoming[0] == BEACON_TYPE_MPB) return !memcmp(cached->data, incoming, 27); if (incoming[0] == BEACON_TYPE_SNB) return !memcmp(cached->data, incoming, 22); return false; } uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi, bool *ivu, bool *kr) { struct net_key *key; struct beacon_rx *beacon; uint32_t b_id, b_ivi; bool b_ivu, b_kr; if (data[1] == BEACON_TYPE_SNB && len != 23) return 0; if (data[1] == BEACON_TYPE_MPB && len != 28) return 0; beacon = l_queue_remove_if(beacons, match_beacon, data + 1); if (beacon) goto accept; /* Validate beacon data */ if (data[1] == BEACON_TYPE_SNB) { key = l_queue_find(keys, match_network, data + 3); if (!key) return 0; b_id = key->id; b_ivu = !!(data[2] & 0x02); b_kr = !!(data[2] & 0x01); b_ivi = l_get_be32(data + 11); if (!net_key_snb_check(b_id, b_ivi, b_kr, b_ivu, l_get_be64(data + 15))) return 0; } else if (data[1] == BEACON_TYPE_MPB) { b_id = private_beacon_check(data + 1, &b_ivi, &b_ivu, &b_kr); if (!b_id) return 0; } else return 0; beacon = l_new(struct beacon_rx, 1); memcpy(beacon->data, data + 1, len - 1); beacon->id = b_id; beacon->ivi = b_ivi; beacon->ivu = b_ivu; beacon->kr = b_kr; accept: *ivi = beacon->ivi; *ivu = beacon->ivu; *kr = beacon->kr; l_queue_push_head(beacons, beacon); return beacon->id; } static void send_network_beacon(struct net_key *key) { struct mesh_io_send_info info = { .type = MESH_IO_TIMING_TYPE_GENERAL, .u.gen.interval = 100, .u.gen.cnt = 1, .u.gen.min_delay = DEFAULT_MIN_DELAY, .u.gen.max_delay = DEFAULT_MAX_DELAY }; if (key->mpb_enables) { /* If Interval steps == 0, refresh key every time */ if (!key->mpb_refresh || !key->mpb || !key->mpb[0]) net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); mesh_io_send(NULL, &info, key->mpb, 28); } if (key->snb_enables) { if (!key->snb || !key->snb[0]) { net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); } mesh_io_send(NULL, &info, key->snb, 23); } } static void beacon_timeout(struct l_timeout *timeout, void *user_data) { struct net_key *key = user_data; uint32_t interval, scale_factor; /* Always send at least one beacon */ send_network_beacon(key); /* Count our own beacons towards the vicinity total */ key->observe.seen++; if (!key->observe.half_period) { l_debug("beacon %d for %d nodes, period %d, obs %d, exp %d", key->id, key->snb_enables + key->mpb_enables, key->observe.period, key->observe.seen, key->observe.expected); interval = (key->observe.period * key->observe.seen) / key->observe.expected; /* Limit Increases and Decreases by 10 seconds Up and * 20 seconds down each step, to avoid going nearly silent * in highly populated environments. */ if (interval - 10 > key->observe.period) interval = key->observe.period + 10; else if (interval + 20 < key->observe.period) interval = key->observe.period - 20; /* Beaconing must be no *slower* than once every 10 minutes, * and no *faster* than once every 10 seconds, per spec. * Observation period is twice beaconing period. */ if (interval < BEACON_INTERVAL_MIN * 2) interval = BEACON_INTERVAL_MIN * 2; else if (interval > BEACON_INTERVAL_MAX * 2) interval = BEACON_INTERVAL_MAX * 2; key->observe.period = interval; key->observe.seen = 0; /* To prevent "over slowing" of the beaconing frequency, * require more significant "over observing" the slower * our own beaconing frequency. */ key->observe.expected = interval / 10; scale_factor = interval / 60; key->observe.expected += scale_factor * 3; } interval = key->observe.period / 2; key->observe.half_period = !key->observe.half_period; if (key->mpb_enables || key->snb_enables) l_timeout_modify(timeout, interval); else { l_timeout_remove(timeout); key->observe.timeout = NULL; } } void net_key_beacon_seen(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) { key->observe.seen++; key->observe.ts = get_timestamp_secs(); } } uint32_t net_key_beacon_last_seen(uint32_t id) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (key) return key->observe.ts; return 0; } bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu, bool force) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool refresh = force; uint32_t rand_ms; if (!key) return false; if (key->snb_enables && !key->snb) { key->snb = l_new(uint8_t, 23); refresh = true; } if (key->mpb_enables && !key->mpb) { key->mpb = l_new(uint8_t, 28); refresh = true; } if (key->ivi != ivi || key->ivu != ivu || key->kr != kr) refresh = true; if (!refresh) return true; if (key->mpb) { if (!mpb_compose(key, ivi, kr, ivu)) return false; print_packet("Set MPB to", key->mpb, 28); } if (key->snb) { if (!snb_compose(key, ivi, kr, ivu)) return false; print_packet("Set SNB to", key->snb, 23); } l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr); key->ivi = ivi; key->ivu = ivu; key->kr = kr; /* Propagate changes to all local nodes */ net_local_beacon(id, ivi, ivu, kr); /* Send one new SNB soon, after all nodes have seen it */ l_getrandom(&rand_ms, sizeof(rand_ms)); rand_ms %= 1000; key->observe.expected++; if (key->observe.timeout) l_timeout_modify_ms(key->observe.timeout, 500 + rand_ms); else key->observe.timeout = l_timeout_create_ms(500 + rand_ms, beacon_timeout, key, NULL); return true; } static void mpb_timeout(struct l_timeout *timeout, void *user_data) { struct net_key *key = user_data; if (key->mpb_refresh) { l_debug("Refresh in %d seconds", key->mpb_refresh * 10); l_timeout_modify(timeout, key->mpb_refresh * 10); } net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true); } void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); bool enabled; uint32_t rand_ms; if (!key) return; enabled = !!key->snb_enables || !!key->mpb_enables; if (mpb) { key->mpb_enables++; key->mpb_refresh = refresh_count; l_timeout_remove(key->mpb_to); if (refresh_count) key->mpb_to = l_timeout_create(refresh_count * 10, mpb_timeout, key, NULL); else key->mpb_to = NULL; } else key->snb_enables++; /* If already Enabled, do nothing */ if (enabled) return; /* Randomize first timeout to avoid bursts of beacons */ l_getrandom(&rand_ms, sizeof(rand_ms)); rand_ms %= (BEACON_INTERVAL_MIN * 1000); rand_ms++; /* Enable Periodic Beaconing on this key */ key->observe.period = BEACON_INTERVAL_MIN * 2; key->observe.expected = 2; key->observe.seen = 0; key->observe.half_period = true; l_timeout_remove(key->observe.timeout); key->observe.timeout = l_timeout_create_ms(rand_ms, beacon_timeout, key, NULL); } void net_key_beacon_disable(uint32_t id, bool mpb) { struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id)); if (!key) return; if (mpb) { if (!key->mpb_enables) return; key->mpb_enables--; if (!key->mpb_enables) { l_free(key->mpb); key->mpb = NULL; l_timeout_remove(key->mpb_to); key->mpb_to = NULL; } } else { if (!key->snb_enables) return; key->snb_enables--; if (!key->snb_enables) { l_free(key->snb); key->snb = NULL; } } if (key->snb_enables || key->mpb_enables) return; /* Disable periodic Beaconing on this key */ l_timeout_remove(key->observe.timeout); key->observe.timeout = NULL; } static void free_key(void *data) { struct net_key *key = data; l_timeout_remove(key->mpb_to); l_free(key->snb); l_free(key->mpb); l_free(key); } void net_key_cleanup(void) { l_queue_destroy(keys, free_key); keys = NULL; l_queue_destroy(beacons, l_free); beacons = NULL; }