xref: /btstack/src/mesh/mesh_proxy.c (revision 7cdc89a533ca236b2c2564b759993b788bae89d3)
1 /*
2  * Copyright (C) 2019 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
24  * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "mesh_proxy.c"
39 
40 #include "mesh/mesh_proxy.h"
41 
42 #include <string.h>
43 
44 #include "bluetooth_company_id.h"
45 #include "bluetooth_data_types.h"
46 #include "bluetooth_gatt.h"
47 #include "btstack_config.h"
48 #include "btstack_crypto.h"
49 #include "btstack_debug.h"
50 #include "btstack_memory.h"
51 #include "btstack_run_loop.h"
52 #include "btstack_util.h"
53 
54 #include "mesh/adv_bearer.h"
55 #include "mesh/gatt_bearer.h"
56 #include "mesh/mesh_crypto.h"
57 #include "mesh/mesh_foundation.h"
58 #include "mesh/mesh_iv_index_seq_number.h"
59 #include "mesh/mesh_lower_transport.h"
60 #include "mesh/mesh_network.h"
61 #include "mesh/mesh_node.h"
62 
63 static void mesh_proxy_start_advertising_with_node_id_next_subnet(void);
64 static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet);
65 
66 #ifdef ENABLE_MESH_PROXY_SERVER
67 
68 // we only support a single active node id advertisement. when new one is started, an active one is stopped
69 
70 #define MESH_PROXY_NODE_ID_ADVERTISEMENT_TIMEOUT_MS 60000
71 
72 static btstack_timer_source_t                           mesh_proxy_node_id_timer;
73 static btstack_crypto_random_t                          mesh_proxy_node_id_crypto_request_random;
74 static btstack_crypto_aes128_t                          mesh_proxy_node_id_crypto_request_aes128;
75 static uint8_t                                          mesh_proxy_node_id_plaintext[16];
76 static uint8_t                                          mesh_proxy_node_id_hash[16];
77 static uint8_t                                          mesh_proxy_node_id_random_value[8];
78 static uint8_t                                          mesh_proxy_node_id_all_subnets;
79 
80 static uint16_t primary_element_address;
81 
82 // Mesh Proxy, advertise unprovisioned device
83 static adv_bearer_connectable_advertisement_data_item_t connectable_advertisement_unprovisioned_device;
84 
85 static const uint8_t adv_data_with_node_id_template[] = {
86     // Flags general discoverable, BR/EDR not supported
87     0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
88     // 16-bit Service UUIDs
89     0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
90     // Service Data
91     0x14, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
92           // MESH_IDENTIFICATION_NODE_IDENTIFY_TYPE
93           MESH_IDENTIFICATION_NODE_IDENTIFY_TYPE,
94           // Hash - 8 bytes
95           // Random - 8 bytes
96 };
97 
98 static const uint8_t adv_data_with_network_id_template[] = {
99     // Flags general discoverable, BR/EDR not supported
100     0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
101     // 16-bit Service UUIDs
102     0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
103     // Service Data
104     0x0C, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROXY & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROXY >> 8,
105           // MESH_IDENTIFICATION_NETWORK_ID_TYPE
106           MESH_IDENTIFICATION_NETWORK_ID_TYPE
107 };
108 
109 #ifdef ENABLE_MESH_PB_GATT
110 
111 // Mesh Provisioning
112 static const uint8_t adv_data_unprovisioned_template[] = {
113     // Flags general discoverable, BR/EDR not supported
114     0x02, BLUETOOTH_DATA_TYPE_FLAGS, 0x06,
115     // 16-bit Service UUIDs
116     0x03, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_16_BIT_SERVICE_CLASS_UUIDS, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING >> 8,
117     // Service Data (22)
118     0x15, BLUETOOTH_DATA_TYPE_SERVICE_DATA, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING & 0xff, ORG_BLUETOOTH_SERVICE_MESH_PROVISIONING >> 8,
119           // UUID - 16 bytes
120           // OOB information - 2 bytes
121 };
122 const uint8_t adv_data_unprovisioned_template_len = sizeof(adv_data_unprovisioned_template);
123 
124 static void mesh_proxy_setup_advertising_unprovisioned(adv_bearer_connectable_advertisement_data_item_t * advertisement_item, const uint8_t * device_uuid) {
125     // store in advertisement item
126     memset(advertisement_item, 0, sizeof(adv_bearer_connectable_advertisement_data_item_t));
127     advertisement_item->adv_length = adv_data_unprovisioned_template_len + 18;
128     (void)memcpy(advertisement_item->adv_data,
129                  (uint8_t *)adv_data_unprovisioned_template,
130                  adv_data_unprovisioned_template_len);
131     // dynamically store device uuid into adv data
132     (void)memcpy(&advertisement_item->adv_data[11], device_uuid, 16);
133     little_endian_store_16(advertisement_item->adv_data, 27, 0);
134 }
135 
136 void mesh_proxy_start_advertising_unprovisioned_device(void){
137     const uint8_t * device_uuid = mesh_node_get_device_uuid();
138     mesh_proxy_setup_advertising_unprovisioned(&connectable_advertisement_unprovisioned_device, device_uuid);
139     // setup advertisements
140     adv_bearer_advertisements_add_item(&connectable_advertisement_unprovisioned_device);
141     adv_bearer_advertisements_enable(1);
142 }
143 
144 /**
145  * @brief Start Advertising Unprovisioned Device with Device ID
146  * @param device_uuid
147  */
148 void mesh_proxy_stop_advertising_unprovisioned_device(void){
149     adv_bearer_advertisements_remove_item(&connectable_advertisement_unprovisioned_device);
150 }
151 
152 #endif
153 
154 static uint8_t mesh_proxy_setup_advertising_with_network_id(uint8_t * buffer, uint8_t * network_id){
155     (void)memcpy(&buffer[0], adv_data_with_network_id_template, 12);
156     (void)memcpy(&buffer[12], network_id, 8);
157     return 20;
158 }
159 
160 static void mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
161     if (mesh_subnet->node_id_advertisement_running != 0){
162         adv_bearer_advertisements_remove_item(&mesh_subnet->advertisement_with_node_id);
163         mesh_subnet->node_id_advertisement_running = 0;
164     }
165 }
166 
167 static void mesh_proxy_stop_all_advertising_with_node_id(void){
168     mesh_subnet_iterator_t it;
169     mesh_subnet_iterator_init(&it);
170     while (mesh_subnet_iterator_has_more(&it)){
171         mesh_subnet_t * mesh_subnet = mesh_subnet_iterator_get_next(&it);
172         mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
173     }
174     btstack_run_loop_remove_timer(&mesh_proxy_node_id_timer);
175     mesh_proxy_node_id_all_subnets = 0;
176 }
177 
178 static void mesh_proxy_node_id_timeout_handler(btstack_timer_source_t * ts){
179     UNUSED(ts);
180     mesh_proxy_stop_all_advertising_with_node_id();
181 }
182 
183 static void mesh_proxy_node_id_handle_get_aes128(void * arg){
184     mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
185 
186     (void)memcpy(mesh_subnet->advertisement_with_node_id.adv_data,
187                  adv_data_with_node_id_template, 12);
188     (void)memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[12],
189                  &mesh_proxy_node_id_hash[8], 8);
190     (void)memcpy(&mesh_subnet->advertisement_with_node_id.adv_data[20],
191                  mesh_proxy_node_id_random_value, 8);
192     mesh_subnet->advertisement_with_node_id.adv_length = 28;
193 
194     // setup advertisements
195     adv_bearer_advertisements_add_item(&mesh_subnet->advertisement_with_node_id);
196     adv_bearer_advertisements_enable(1);
197 
198     // set timer
199     btstack_run_loop_set_timer_handler(&mesh_proxy_node_id_timer, mesh_proxy_node_id_timeout_handler);
200     btstack_run_loop_set_timer(&mesh_proxy_node_id_timer, MESH_PROXY_NODE_ID_ADVERTISEMENT_TIMEOUT_MS);
201     btstack_run_loop_add_timer(&mesh_proxy_node_id_timer);
202 
203     // mark as active
204     mesh_subnet->node_id_advertisement_running = 1;
205 
206     // next one
207     if (mesh_proxy_node_id_all_subnets == 0) return;
208     mesh_proxy_start_advertising_with_node_id_next_subnet();
209 }
210 
211 static void mesh_proxy_node_id_handle_random(void * arg){
212     mesh_subnet_t * mesh_subnet = (mesh_subnet_t *) arg;
213 
214     // Hash = e(IdentityKey, Padding | Random | Address) mod 2^64
215     memset(mesh_proxy_node_id_plaintext, 0, sizeof(mesh_proxy_node_id_plaintext));
216     (void)memcpy(&mesh_proxy_node_id_plaintext[6],
217                  mesh_proxy_node_id_random_value, 8);
218     big_endian_store_16(mesh_proxy_node_id_plaintext, 14, primary_element_address);
219     // TODO: old vs. new key
220     btstack_crypto_aes128_encrypt(&mesh_proxy_node_id_crypto_request_aes128, mesh_subnet->old_key->identity_key, mesh_proxy_node_id_plaintext, mesh_proxy_node_id_hash, mesh_proxy_node_id_handle_get_aes128, mesh_subnet);
221 }
222 
223 static void mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet_t * mesh_subnet){
224     if (mesh_subnet->node_id_advertisement_running) return;
225     log_info("Proxy start advertising with node id, netkey index %04x", mesh_subnet->netkey_index);
226     // setup node id
227     btstack_crypto_random_generate(&mesh_proxy_node_id_crypto_request_random, mesh_proxy_node_id_random_value, sizeof(mesh_proxy_node_id_random_value), mesh_proxy_node_id_handle_random, mesh_subnet);
228 }
229 
230 // Public API
231 
232 uint8_t mesh_proxy_get_advertising_with_node_id_status(uint16_t netkey_index, mesh_node_identity_state_t * out_state ){
233     mesh_subnet_t * mesh_subnet = mesh_subnet_get_by_netkey_index(netkey_index);
234     if (mesh_subnet == NULL){
235         *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED;
236         return MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
237     }
238 
239 #ifdef ENABLE_MESH_PROXY_SERVER
240     if (mesh_subnet->node_id_advertisement_running == 0){
241         *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED;
242     } else {
243         *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING;
244     }
245 #else
246     *out_state = MESH_NODE_IDENTITY_STATE_ADVERTISING_NOT_SUPPORTED;
247 #endif
248 
249     return MESH_FOUNDATION_STATUS_SUCCESS;
250 }
251 
252 uint8_t mesh_proxy_set_advertising_with_node_id(uint16_t netkey_index, mesh_node_identity_state_t state){
253     mesh_subnet_t * mesh_subnet = mesh_subnet_get_by_netkey_index(netkey_index);
254     if (mesh_subnet == NULL){
255         return  MESH_FOUNDATION_STATUS_INVALID_NETKEY_INDEX;
256     }
257 
258 #ifdef ENABLE_MESH_PROXY_SERVER
259     switch (state){
260         case MESH_NODE_IDENTITY_STATE_ADVERTISING_STOPPED:
261             mesh_proxy_stop_advertising_with_node_id_for_subnet(mesh_subnet);
262             return MESH_FOUNDATION_STATUS_SUCCESS;
263         case MESH_NODE_IDENTITY_STATE_ADVERTISING_RUNNING:
264             mesh_proxy_start_advertising_with_node_id_for_subnet(mesh_subnet);
265             return MESH_FOUNDATION_STATUS_SUCCESS;
266         default:
267             break;
268     }
269 #endif
270 
271     return MESH_FOUNDATION_STATUS_FEATURE_NOT_SUPPORTED;
272 }
273 
274 static void mesh_proxy_start_advertising_with_node_id_next_subnet(void){
275     mesh_subnet_iterator_t it;
276     mesh_subnet_iterator_init(&it);
277     while (mesh_subnet_iterator_has_more(&it)){
278         mesh_subnet_t * subnet = mesh_subnet_iterator_get_next(&it);
279         if (subnet->node_id_advertisement_running != 0) continue;
280 
281         mesh_proxy_start_advertising_with_node_id_for_subnet(subnet);
282         return;
283     }
284 }
285 
286 void mesh_proxy_start_advertising_with_node_id(void){
287     mesh_proxy_node_id_all_subnets = 1;
288     adv_bearer_advertisements_enable(1);
289 
290     // start advertising on first subnet that is not already advertising with node id
291     mesh_proxy_start_advertising_with_node_id_next_subnet();
292 }
293 
294 void mesh_proxy_stop_advertising_with_node_id(void){
295     mesh_proxy_stop_all_advertising_with_node_id();
296 }
297 
298 void mesh_proxy_start_advertising_with_network_id(void){
299     mesh_subnet_iterator_t it;
300     mesh_subnet_iterator_init(&it);
301     while (mesh_subnet_iterator_has_more(&it)){
302         mesh_subnet_t * subnet = mesh_subnet_iterator_get_next(&it);
303         log_info("Proxy start advertising with network id, netkey index %04x", subnet->netkey_index);
304         // setup advertisement with network id (used by proxy)
305         mesh_network_key_t * network_key = mesh_subnet_get_outgoing_network_key(subnet);
306         subnet->advertisement_with_network_id.adv_length = mesh_proxy_setup_advertising_with_network_id(subnet->advertisement_with_network_id.adv_data, network_key->network_id);
307         adv_bearer_advertisements_add_item(&subnet->advertisement_with_network_id);
308     }
309     adv_bearer_advertisements_enable(1);
310 }
311 
312 void mesh_proxy_stop_advertising_with_network_id(void){
313     mesh_subnet_iterator_t it;
314     mesh_subnet_iterator_init(&it);
315     while (mesh_subnet_iterator_has_more(&it)){
316         mesh_subnet_t * network_key = mesh_subnet_iterator_get_next(&it);
317         log_info("Proxy stop advertising with network id, netkey index %04x", network_key->netkey_index);
318         adv_bearer_advertisements_remove_item(&network_key->advertisement_with_network_id);
319     }
320 }
321 
322 // Mesh Proxy Configuration
323 
324 typedef enum {
325     MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_SET_FILTER_TYPE = 0,
326     MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_ADD_ADDRESSES,
327     MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_REMOVE_ADDRESSES,
328     MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_FILTER_STATUS
329 } mesh_proxy_configuration_message_opcode_t;
330 
331 typedef enum {
332     MESH_PROXY_CONFIGURATION_FILTER_TYPE_SET_WHITE_LIST = 0,
333     MESH_PROXY_CONFIGURATION_FILTER_TYPE_BLACK_LIST
334 } mesh_proxy_configuration_filter_type_t;
335 
336 static mesh_network_pdu_t * encrypted_proxy_configuration_ready_to_send;
337 
338 // Used to answer configuration request
339 static uint16_t proxy_configuration_filter_list_len;
340 static mesh_proxy_configuration_filter_type_t proxy_configuration_filter_type;
341 static uint16_t primary_element_address;
342 
343 static void request_can_send_now_proxy_configuration_callback_handler(mesh_network_pdu_t * network_pdu){
344     encrypted_proxy_configuration_ready_to_send = network_pdu;
345     gatt_bearer_request_can_send_now_for_mesh_proxy_configuration();
346 }
347 
348 static void proxy_configuration_message_handler(mesh_network_callback_type_t callback_type, mesh_network_pdu_t * received_network_pdu){
349     mesh_proxy_configuration_message_opcode_t opcode;
350     uint8_t data[4];
351     mesh_network_pdu_t * network_pdu;
352     uint8_t * network_pdu_data;
353 
354     switch (callback_type){
355         case MESH_NETWORK_PDU_RECEIVED:
356             printf("proxy_configuration_message_handler: MESH_PROXY_PDU_RECEIVED\n");
357             network_pdu_data = mesh_network_pdu_data(received_network_pdu);
358             opcode = network_pdu_data[0];
359             switch (opcode){
360                 case MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_SET_FILTER_TYPE:{
361                     switch (network_pdu_data[1]){
362                         case MESH_PROXY_CONFIGURATION_FILTER_TYPE_SET_WHITE_LIST:
363                         case MESH_PROXY_CONFIGURATION_FILTER_TYPE_BLACK_LIST:
364                             proxy_configuration_filter_type = network_pdu_data[1];
365                             break;
366                     }
367 
368                     uint8_t  ctl          = 1;
369                     uint8_t  ttl          = 0;
370                     uint16_t src          = primary_element_address;
371                     uint16_t dest         = 0; // unassigned address
372                     uint32_t seq          = mesh_sequence_number_next();
373                     uint8_t  nid          = mesh_network_nid(received_network_pdu);
374                     uint16_t netkey_index = received_network_pdu->netkey_index;
375                     printf("netkey index 0x%02x\n", netkey_index);
376 
377                     network_pdu = btstack_memory_mesh_network_pdu_get();
378                     int pos = 0;
379                     data[pos++] = MESH_PROXY_CONFIGURATION_MESSAGE_OPCODE_FILTER_STATUS;
380                     data[pos++] = proxy_configuration_filter_type;
381                     big_endian_store_16(data, pos, proxy_configuration_filter_list_len);
382 
383                     mesh_network_setup_pdu(network_pdu, netkey_index, nid, ctl, ttl, seq, src, dest, data, sizeof(data));
384                     mesh_network_encrypt_proxy_configuration_message(network_pdu, &request_can_send_now_proxy_configuration_callback_handler);
385 
386                     // received_network_pdu is processed
387                     btstack_memory_mesh_network_pdu_free(received_network_pdu);
388                     break;
389                 }
390                 default:
391                     printf("proxy config not implemented, opcode %d\n", opcode);
392                     break;
393             }
394             break;
395         case MESH_NETWORK_CAN_SEND_NOW:
396             printf("MESH_SUBEVENT_CAN_SEND_NOW mesh_netework_gatt_bearer_handle_proxy_configuration len %d\n", encrypted_proxy_configuration_ready_to_send->len);
397             printf_hexdump(encrypted_proxy_configuration_ready_to_send->data, encrypted_proxy_configuration_ready_to_send->len);
398             gatt_bearer_send_mesh_proxy_configuration(encrypted_proxy_configuration_ready_to_send->data, encrypted_proxy_configuration_ready_to_send->len);
399             break;
400         case MESH_NETWORK_PDU_SENT:
401             // printf("test MESH_PROXY_PDU_SENT\n");
402             // mesh_lower_transport_received_mesage(MESH_NETWORK_PDU_SENT, network_pdu);
403             break;
404         default:
405             break;
406     }
407 }
408 
409 void mesh_proxy_init(uint16_t primary_unicast_address){
410     primary_element_address = primary_unicast_address;
411 
412     // mesh proxy configuration
413     mesh_network_set_proxy_message_handler(proxy_configuration_message_handler);
414 }
415 #endif
416 
417