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