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