xref: /btstack/src/ble/att_db_util.c (revision c9921182ab4b1f83e3e5c671446dca5ffdf45b90)
1 /*
2  * Copyright (C) 2014 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__ "att_db_util.c"
39 
40 #include <string.h>
41 #include <stdlib.h>
42 
43 #include "ble/att_db_util.h"
44 #include "ble/att_db.h"
45 #include "ble/core.h"
46 #include "btstack_util.h"
47 #include "btstack_debug.h"
48 #include "bluetooth.h"
49 
50 // ATT DB Storage
51 #ifdef HAVE_MALLOC
52 // number of bytes that the att db buffer is increased on init / realloc
53 #define ATT_DB_BUFFER_INCREMENT 128
54 #else
55 #ifdef MAX_ATT_DB_SIZE
56 static uint8_t att_db_storage[MAX_ATT_DB_SIZE];
57 #else
58 #error Neither HAVE_MALLOC nor MAX_ATT_DB_SIZE is defined.
59 #endif
60 #endif
61 
62 static uint8_t * att_db;
63 static uint16_t  att_db_size;
64 static uint16_t  att_db_max_size;
65 static uint16_t  att_db_next_handle;
66 static uint16_t  att_db_hash_len;
67 
68 static void att_db_util_set_end_tag(void){
69 	// end tag
70 	att_db[att_db_size] = 0;
71 	att_db[att_db_size+1u] = 0u;
72 }
73 
74 void att_db_util_init(void){
75 #ifdef HAVE_MALLOC
76 	att_db = (uint8_t*) malloc(ATT_DB_BUFFER_INCREMENT);
77 	att_db_max_size = ATT_DB_BUFFER_INCREMENT;
78 #else
79 	att_db = att_db_storage;
80 	att_db_max_size = sizeof(att_db_storage);
81 #endif
82 	// store att version
83 	att_db[0] = ATT_DB_VERSION;
84 	att_db_size = 1;
85 	att_db_next_handle = 1;
86 	att_db_hash_len = 0;
87 	att_db_util_set_end_tag();
88 }
89 
90 static bool att_db_util_hash_include_with_value(uint16_t uuid16){
91     /* «Primary Service», «Secondary Service», «Included Service», «Characteristic», or «Characteristic Extended Properties» */
92     switch (uuid16){
93         case GATT_PRIMARY_SERVICE_UUID:
94         case GATT_SECONDARY_SERVICE_UUID:
95         case GATT_INCLUDE_SERVICE_UUID:
96         case GATT_CHARACTERISTICS_UUID:
97         case GATT_CHARACTERISTIC_EXTENDED_PROPERTIES:
98             return true;
99         default:
100             return false;
101     }
102 }
103 
104 static bool att_db_util_hash_include_without_value(uint16_t uuid16){
105     /*  «Characteristic User Description», «Client Characteristic Configuration», «Server Characteristic Configuration»,
106      * «Characteristic Aggregate Format», «Characteristic Format» */
107     switch (uuid16){
108         case GATT_CHARACTERISTIC_USER_DESCRIPTION:
109         case GATT_CLIENT_CHARACTERISTICS_CONFIGURATION:
110         case GATT_SERVER_CHARACTERISTICS_CONFIGURATION:
111         case GATT_CHARACTERISTIC_PRESENTATION_FORMAT:
112         case GATT_CHARACTERISTIC_AGGREGATE_FORMAT:
113             return true;
114         default:
115             return false;
116     }
117 }
118 
119 /**
120  * asserts that the requested amount of bytes can be stored in the att_db
121  * @returns TRUE if space is available
122  */
123 static int att_db_util_assert_space(uint16_t size){
124 	size += 2u; // for end tag
125 	uint16_t required_size = att_db_size + size;
126 	if (required_size <= att_db_max_size) return 1;
127 #ifdef HAVE_MALLOC
128     uint16_t new_size = att_db_max_size;
129 	while (new_size < required_size){
130         new_size += ATT_DB_BUFFER_INCREMENT;
131 	}
132 	uint8_t * new_db = (uint8_t*) realloc(att_db, new_size);
133 	if (!new_db) {
134 		log_error("att_db: realloc failed");
135 		return 0;
136 	}
137 	att_db = new_db;
138 	att_db_max_size = new_size;
139     att_set_db(att_db); // Update att_db with the new db
140 	return 1;
141 #else
142 	log_error("att_db: out of memory");
143 	return 0;
144 #endif
145 }
146 
147 // attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...)
148 
149 // db endds with 0x00 0x00
150 
151 static void att_db_util_add_attribute_uuid16(uint16_t uuid16, uint16_t flags, uint8_t * data, uint16_t data_len){
152 	int size = 2u + 2u + 2u + 2u + data_len;
153 	if (!att_db_util_assert_space(size)) return;
154 	little_endian_store_16(att_db, att_db_size, size);
155 	att_db_size += 2u;
156 	little_endian_store_16(att_db, att_db_size, flags);
157 	att_db_size += 2u;
158 	little_endian_store_16(att_db, att_db_size, att_db_next_handle);
159 	att_db_size += 2u;
160 	att_db_next_handle++;
161 	little_endian_store_16(att_db, att_db_size, uuid16);
162 	att_db_size += 2u;
163 	(void)memcpy(&att_db[att_db_size], data, data_len);
164 	att_db_size += data_len;
165 	att_db_util_set_end_tag();
166 
167 	if (att_db_util_hash_include_with_value(uuid16)){
168 	    att_db_hash_len += 4u + data_len;
169 	} else if (att_db_util_hash_include_without_value(uuid16)){
170         att_db_hash_len += 4u;
171 	}
172 }
173 
174 static void att_db_util_add_attribute_uuid128(const uint8_t * uuid128, uint16_t flags, uint8_t * data, uint16_t data_len){
175 	int size = 2u + 2u + 2u + 16u + data_len;
176 	if (!att_db_util_assert_space(size)) return;
177 	flags |= ATT_PROPERTY_UUID128;
178 	little_endian_store_16(att_db, att_db_size, size);
179 	att_db_size += 2u;
180 	little_endian_store_16(att_db, att_db_size, flags);
181 	att_db_size += 2u;
182 	little_endian_store_16(att_db, att_db_size, att_db_next_handle);
183 	att_db_size += 2u;
184 	att_db_next_handle++;
185 	reverse_128(uuid128, &att_db[att_db_size]);
186 	att_db_size += 16u;
187 	(void)memcpy(&att_db[att_db_size], data, data_len);
188 	att_db_size += data_len;
189 	att_db_util_set_end_tag();
190 }
191 
192 uint16_t att_db_util_add_service_uuid16(uint16_t uuid16){
193 	uint8_t buffer[2];
194 	little_endian_store_16(buffer, 0, uuid16);
195 	uint16_t service_handle = att_db_next_handle;
196 	att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 2);
197 	return service_handle;
198 }
199 
200 uint16_t att_db_util_add_service_uuid128(const uint8_t * uuid128){
201 	uint8_t buffer[16];
202 	reverse_128(uuid128, buffer);
203 	uint16_t service_handle = att_db_next_handle;
204 	att_db_util_add_attribute_uuid16(GATT_PRIMARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 16);
205 	return service_handle;
206 }
207 
208 uint16_t att_db_util_add_secondary_service_uuid16(uint16_t uuid16){
209     uint8_t buffer[2];
210     little_endian_store_16(buffer, 0, uuid16);
211     uint16_t service_handle = att_db_next_handle;
212     att_db_util_add_attribute_uuid16(GATT_SECONDARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 2);
213     return service_handle;
214 }
215 
216 uint16_t att_db_util_add_secondary_service_uuid128(const uint8_t * uuid128){
217     uint8_t buffer[16];
218     reverse_128(uuid128, buffer);
219     uint16_t service_handle = att_db_next_handle;
220     att_db_util_add_attribute_uuid16(GATT_SECONDARY_SERVICE_UUID, ATT_PROPERTY_READ, buffer, 16);
221     return service_handle;
222 }
223 
224 uint16_t att_db_util_add_included_service_uuid16(uint16_t start_group_handle, uint16_t  end_group_handle, uint16_t uuid16){
225     uint8_t buffer[6];
226     little_endian_store_16(buffer, 0, start_group_handle);
227     little_endian_store_16(buffer, 2, end_group_handle);
228     little_endian_store_16(buffer, 4, uuid16);
229     uint16_t service_handle = att_db_next_handle;
230     att_db_util_add_attribute_uuid16(GATT_INCLUDE_SERVICE_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer));
231     return service_handle;
232 }
233 
234 static void att_db_util_add_client_characteristic_configuration(uint16_t flags){
235 	uint8_t buffer[2];
236 	// drop permission for read (0xc00), keep write permissions (0x0091)
237 	flags = (flags & 0x1f391u) | ATT_PROPERTY_READ | ATT_PROPERTY_WRITE | ATT_PROPERTY_DYNAMIC;
238 	little_endian_store_16(buffer, 0, 0);
239 	att_db_util_add_attribute_uuid16(GATT_CLIENT_CHARACTERISTICS_CONFIGURATION, flags, buffer, 2);
240 }
241 
242 static uint16_t att_db_util_encode_permissions(uint16_t properties, uint8_t read_permission, uint8_t write_permission){
243     // drop Broadcast (0x01), Notify (0x10), Indicate (0x20), Extended Attributes (0x80) - not used for flags
244     uint16_t flags = properties & 0xfff4eu;
245     // if encryption requested, set encryption key size to 16
246     if ((read_permission > ATT_SECURITY_NONE) || (write_permission > ATT_SECURITY_NONE)){
247     	flags |= 0xf000u;
248     }
249     // map SC requirement
250     if (read_permission == ATT_SECURITY_AUTHENTICATED_SC){
251         read_permission =  ATT_SECURITY_AUTHENTICATED;
252         flags |= ATT_PROPERTY_READ_PERMISSION_SC;
253     }
254     if (write_permission == ATT_SECURITY_AUTHENTICATED_SC){
255         write_permission =  ATT_SECURITY_AUTHENTICATED;
256         flags |= ATT_PROPERTY_WRITE_PERMISSION_SC;
257     }
258     // encode read/write security levels
259     if (read_permission & 1u){
260     	flags |= ATT_PROPERTY_READ_PERMISSION_BIT_0;
261     }
262     if (read_permission & 2u){
263     	flags |= ATT_PROPERTY_READ_PERMISSION_BIT_1;
264     }
265     if (write_permission & 1u){
266     	flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_0;
267     }
268     if (write_permission & 2u){
269     	flags |= ATT_PROPERTY_WRITE_PERMISSION_BIT_1;
270     }
271 	return flags;
272 }
273 
274 uint16_t att_db_util_add_characteristic_uuid16(uint16_t uuid16, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){
275 	uint8_t buffer[5];
276 	buffer[0] = properties;
277 	little_endian_store_16(buffer, 1u, att_db_next_handle + 1u);
278 	little_endian_store_16(buffer, 3, uuid16);
279 	att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer));
280 	uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission);
281 	uint16_t value_handle = att_db_next_handle;
282 	att_db_util_add_attribute_uuid16(uuid16, flags, data, data_len);
283 	if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){
284 		att_db_util_add_client_characteristic_configuration(flags);
285 	}
286 	return value_handle;
287 }
288 
289 uint16_t att_db_util_add_characteristic_uuid128(const uint8_t * uuid128, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){
290 	uint8_t buffer[19];
291 	buffer[0] = properties;
292 	little_endian_store_16(buffer, 1u, att_db_next_handle + 1u);
293 	reverse_128(uuid128, &buffer[3]);
294 	att_db_util_add_attribute_uuid16(GATT_CHARACTERISTICS_UUID, ATT_PROPERTY_READ, buffer, sizeof(buffer));
295 	uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission);
296 	uint16_t value_handle = att_db_next_handle;
297 	att_db_util_add_attribute_uuid128(uuid128, flags, data, data_len);
298 	if (properties & (ATT_PROPERTY_NOTIFY | ATT_PROPERTY_INDICATE)){
299 		att_db_util_add_client_characteristic_configuration(flags);
300 	}
301 	return value_handle;
302 }
303 
304 uint16_t att_db_util_add_descriptor_uuid16(uint16_t uuid16, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){
305     uint16_t descriptor_handler = att_db_next_handle;
306 	uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission);
307     att_db_util_add_attribute_uuid16(uuid16, flags, data, data_len);
308     return descriptor_handler;
309 }
310 
311 uint16_t att_db_util_add_descriptor_uuid128(const uint8_t * uuid128, uint16_t properties, uint8_t read_permission, uint8_t write_permission, uint8_t * data, uint16_t data_len){
312     uint16_t descriptor_handler = att_db_next_handle;
313 	uint16_t flags = att_db_util_encode_permissions(properties, read_permission, write_permission);
314     att_db_util_add_attribute_uuid128(uuid128, flags, data, data_len);
315     return descriptor_handler;
316  }
317 
318 uint8_t * att_db_util_get_address(void){
319 	return att_db;
320 }
321 
322 uint16_t att_db_util_get_size(void){
323 	return att_db_size + 2u;	// end tag
324 }
325 
326 static uint8_t * att_db_util_hash_att_ptr;
327 static uint16_t att_db_util_hash_offset;
328 static uint16_t att_db_util_hash_bytes_available;
329 
330 static void att_db_util_hash_fetch_next_attribute(void){
331     while (1){
332         uint16_t size = little_endian_read_16(att_db_util_hash_att_ptr, 0);
333         btstack_assert(size != 0);
334         UNUSED(size);
335         uint16_t flags = little_endian_read_16(att_db_util_hash_att_ptr, 2);
336         if ((flags & ATT_PROPERTY_UUID128) == 0u) {
337             uint16_t uuid16 = little_endian_read_16(att_db_util_hash_att_ptr, 6);
338             if (att_db_util_hash_include_with_value(uuid16)){
339                 att_db_util_hash_offset = 4;
340                 att_db_util_hash_bytes_available = size - 4u;
341                 return;
342             } else if (att_db_util_hash_include_without_value(uuid16)){
343                 att_db_util_hash_offset = 4;
344                 att_db_util_hash_bytes_available = 4;
345                 return;
346             }
347         }
348         att_db_util_hash_att_ptr += size;
349     }
350 }
351 
352 uint16_t att_db_util_hash_len(void){
353     return att_db_hash_len;
354 }
355 
356 void att_db_util_hash_init(void){
357     // skip version info
358     att_db_util_hash_att_ptr = &att_db[1];
359     att_db_util_hash_bytes_available = 0;
360 }
361 
362 uint8_t att_db_util_hash_get_next(void){
363     // find next hashable data blob
364     if (att_db_util_hash_bytes_available == 0u){
365         att_db_util_hash_fetch_next_attribute();
366     }
367 
368     // get next byte
369     uint8_t next = att_db_util_hash_att_ptr[att_db_util_hash_offset++];
370     att_db_util_hash_bytes_available--;
371 
372     // go to next attribute if blob used up
373     if (att_db_util_hash_bytes_available == 0u){
374         uint16_t size = little_endian_read_16(att_db_util_hash_att_ptr, 0);
375         att_db_util_hash_att_ptr += size;
376     }
377     return next;
378 }
379 
380 static uint8_t att_db_util_hash_get(uint16_t offset){
381     UNUSED(offset);
382     return att_db_util_hash_get_next();
383 }
384 
385 void att_db_util_hash_calc(btstack_crypto_aes128_cmac_t * request, uint8_t * db_hash, void (* callback)(void * arg), void * callback_arg){
386     static const uint8_t zero_key[16] = { 0 };
387     att_db_util_hash_init();
388     btstack_crypto_aes128_cmac_generator(request, zero_key, att_db_hash_len, &att_db_util_hash_get, db_hash, callback, callback_arg);
389 }
390