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