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