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