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