diff --git a/components/bt/common/Kconfig.in b/components/bt/common/Kconfig.in index eec63cbf0ce..7ed4f8c8834 100644 --- a/components/bt/common/Kconfig.in +++ b/components/bt/common/Kconfig.in @@ -53,7 +53,7 @@ endmenu choice BT_SMP_CRYPTO_STACK prompt "SMP cryptographic stack" depends on (BT_BLE_SMP_ENABLE || BT_SMP_ENABLE || BT_NIMBLE_SECURITY_ENABLE || BT_LE_SECURITY_ENABLE || \ - BT_CTRL_BREDR_ENABLE) + BT_CTRL_BREDR_ENABLE || BT_BLE_FEAT_ENC_ADV_DATA) default BT_SMP_CRYPTO_STACK_TINYCRYPT help Select the cryptographic library to use for SMP operations (AES, AES-CMAC, ECDH P-256). diff --git a/components/bt/host/bluedroid/CMakeLists.txt b/components/bt/host/bluedroid/CMakeLists.txt index 81ee5570b25..54d984a1013 100644 --- a/components/bt/host/bluedroid/CMakeLists.txt +++ b/components/bt/host/bluedroid/CMakeLists.txt @@ -355,6 +355,12 @@ if(CONFIG_BT_BLE_FEAT_CTE_EN) ) endif() +if(CONFIG_BT_BLE_FEAT_ENC_ADV_DATA) + list(APPEND bluedroid_host_srcs + "${CMAKE_CURRENT_LIST_DIR}/api/esp_ble_ead.c" + ) +endif() + if(CONFIG_BT_BLE_PERIPH_PSEUDO_ADDR_BOND) list(APPEND bluedroid_host_srcs "${CMAKE_CURRENT_LIST_DIR}/stack/btm/btm_ble_pseudo.c" diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index c4857158cc7..1e8d73f8700 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -425,16 +425,6 @@ config BT_GATTS_SECURITY_LEVELS_CHAR help Enable LE GATT Security Levels Characteristic -config BT_GATTS_KEY_MATERIAL_CHAR - bool "Enable Encrypted Data Key Material Characteristic" - depends on BT_GATTS_ENABLE - default n - help - Enable the Encrypted Data Key Material characteristic in GAP service. - This characteristic allows advertising data to be decrypted and authenticated - using the key material (session key + IV) as defined in Bluetooth Core - Specification Version 5.4. The characteristic requires encrypted link to read. - menuconfig BT_GATTC_ENABLE bool "Include GATT client module(GATTC)" depends on BT_BLE_ENABLED @@ -653,6 +643,36 @@ config BT_BLE_RPA_SUPPORTED For other BLE chips, devices support network privacy mode and device privacy mode, users can switch the two modes according to their own needs. So this option is enabled by default. +menu "Encrypted Advertising Data (EAD)" + depends on BT_BLE_ENABLED + + config BT_BLE_FEAT_ENC_ADV_DATA + bool "Encrypted Advertising Data APIs" + default n + help + Enable AES-CCM encrypt and decrypt APIs (esp_ble_ead_encrypt / + esp_ble_ead_decrypt) for advertising payloads, as defined in + Bluetooth Core Specification 5.4. Uses the same cryptographic + stack as SMP (TinyCrypt or mbedTLS). + + Enable this alone for a scanner/central that decrypts EAD with a + pre-shared key, or when the peer publishes Key Material over GATT. + + config BT_GATTS_KEY_MATERIAL_CHAR + bool "Encrypted Data Key Material Characteristic" + depends on BT_GATTS_ENABLE + select BT_BLE_FEAT_ENC_ADV_DATA + default n + help + Expose session key and IV through the GAP Key Material characteristic + (UUID 0x2B88). A central can read it over an encrypted GATT link and + then decrypt advertising data with esp_ble_ead_decrypt(). + + Selecting this option also enables Encrypted Advertising Data APIs. + Set the published key with esp_ble_gap_set_key_material(). + +endmenu + menu "Bluedroid debug option" config BT_BLUEDROID_MEM_DEBUG bool "Bluedroid memory debug" diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c b/components/bt/host/bluedroid/api/esp_ble_ead.c similarity index 58% rename from examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c rename to components/bt/host/bluedroid/api/esp_ble_ead.c index 59bb3c60103..e93af54ae1b 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.c +++ b/components/bt/host/bluedroid/api/esp_ble_ead.c @@ -1,18 +1,20 @@ /* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include -#include "ble_ead.h" +#include +#include "esp_ble_ead.h" #include "esp_random.h" #include "esp_log.h" #include "sdkconfig.h" +#if (CONFIG_BT_BLE_FEAT_ENC_ADV_DATA) + #define TAG "BLE_EAD" -/* Select crypto library based on configuration */ #if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) #include "tinycrypt/aes.h" #include "tinycrypt/ccm_mode.h" @@ -24,7 +26,7 @@ #endif /* Additional Authenticated Data for EAD - EA (Encrypted Advertising) */ -static const uint8_t ble_ead_aad[BLE_EAD_AAD_SIZE] = { 0xEA }; +static const uint8_t ble_ead_aad[ESP_BLE_EAD_AAD_SIZE] = { 0xEA }; /** * @brief Generate randomizer with direction bit set @@ -32,14 +34,10 @@ static const uint8_t ble_ead_aad[BLE_EAD_AAD_SIZE] = { 0xEA }; * Per Bluetooth Core Spec Supplement v11, Part A 1.23.3: * The MSB of the Randomizer shall be set to indicate direction */ -static int ble_ead_generate_randomizer(uint8_t randomizer[BLE_EAD_RANDOMIZER_SIZE]) +static int ble_ead_generate_randomizer(uint8_t randomizer[ESP_BLE_EAD_RANDOMIZER_SIZE]) { - /* Generate random bytes */ - esp_fill_random(randomizer, BLE_EAD_RANDOMIZER_SIZE); - - /* Set direction bit (MSB of last byte) - required by spec */ - randomizer[BLE_EAD_RANDOMIZER_SIZE - 1] |= (1 << BLE_EAD_RANDOMIZER_DIRECTION_BIT); - + esp_fill_random(randomizer, ESP_BLE_EAD_RANDOMIZER_SIZE); + randomizer[ESP_BLE_EAD_RANDOMIZER_SIZE - 1] |= (1 << ESP_BLE_EAD_RANDOMIZER_DIRECTION_BIT); return 0; } @@ -48,48 +46,39 @@ static int ble_ead_generate_randomizer(uint8_t randomizer[BLE_EAD_RANDOMIZER_SIZ * * Nonce = Randomizer (5 bytes) || IV (8 bytes) = 13 bytes */ -static int ble_ead_generate_nonce(const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t randomizer[BLE_EAD_RANDOMIZER_SIZE], - uint8_t nonce[BLE_EAD_NONCE_SIZE]) +static int ble_ead_generate_nonce(const uint8_t iv[ESP_BLE_EAD_IV_SIZE], + const uint8_t randomizer[ESP_BLE_EAD_RANDOMIZER_SIZE], + uint8_t nonce[ESP_BLE_EAD_NONCE_SIZE]) { if (iv == NULL || nonce == NULL) { return -1; } - /* Randomizer in first 5 bytes */ if (randomizer != NULL) { - memcpy(nonce, randomizer, BLE_EAD_RANDOMIZER_SIZE); + memcpy(nonce, randomizer, ESP_BLE_EAD_RANDOMIZER_SIZE); } else { - /* Generate new randomizer with direction bit */ ble_ead_generate_randomizer(nonce); } - /* IV in last 8 bytes */ - memcpy(nonce + BLE_EAD_RANDOMIZER_SIZE, iv, BLE_EAD_IV_SIZE); - + memcpy(nonce + ESP_BLE_EAD_RANDOMIZER_SIZE, iv, ESP_BLE_EAD_IV_SIZE); return 0; } -/** - * @brief AES-CCM encryption using selected crypto library - */ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, - const uint8_t *plaintext, size_t plaintext_len, - const uint8_t *aad, size_t aad_len, - uint8_t *ciphertext, size_t tag_len) + const uint8_t *plaintext, size_t plaintext_len, + const uint8_t *aad, size_t aad_len, + uint8_t *ciphertext, size_t tag_len) { #if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) struct tc_aes_key_sched_struct sched; struct tc_ccm_mode_struct ccm_state; int ret; - /* Validate inputs */ if (key == NULL || nonce == NULL || ciphertext == NULL) { ESP_LOGE(TAG, "Invalid input parameters"); return -1; } - /* Set AES encryption key */ ret = tc_aes128_set_encrypt_key(&sched, key); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_aes128_set_encrypt_key failed"); @@ -97,12 +86,7 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Configure CCM mode */ - ccm_state.sched = &sched; - ccm_state.nonce = (uint8_t *)nonce; - ccm_state.mlen = tag_len; - - ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, BLE_EAD_NONCE_SIZE, tag_len); + ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, ESP_BLE_EAD_NONCE_SIZE, tag_len); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_ccm_config failed"); memset(&sched, 0, sizeof(sched)); @@ -110,12 +94,10 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Encrypt and generate tag */ - /* TinyCrypt outputs: ciphertext || tag */ ret = tc_ccm_generation_encryption(ciphertext, plaintext_len + tag_len, - aad, aad_len, - plaintext, plaintext_len, - &ccm_state); + aad, aad_len, + plaintext, plaintext_len, + &ccm_state); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_ccm_generation_encryption failed"); memset(&sched, 0, sizeof(sched)); @@ -123,7 +105,6 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Clear sensitive data from key schedule */ memset(&sched, 0, sizeof(sched)); memset(&ccm_state, 0, sizeof(ccm_state)); return 0; @@ -135,31 +116,38 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_len); size_t output_length = 0; - /* Set key attributes */ + if (key == NULL || nonce == NULL || ciphertext == NULL) { + ESP_LOGE(TAG, "Invalid input parameters"); + return -1; + } + + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + ESP_LOGE(TAG, "psa_crypto_init failed: %d", (int)status); + return -1; + } + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); psa_set_key_algorithm(&attributes, alg); psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attributes, BLE_EAD_KEY_SIZE * 8); + psa_set_key_bits(&attributes, ESP_BLE_EAD_KEY_SIZE * 8); - /* Import key */ - status = psa_import_key(&attributes, key, BLE_EAD_KEY_SIZE, &key_id); + status = psa_import_key(&attributes, key, ESP_BLE_EAD_KEY_SIZE, &key_id); if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_import_key failed: %d", status); + ESP_LOGE(TAG, "psa_import_key failed: %d", (int)status); psa_reset_key_attributes(&attributes); return -1; } psa_reset_key_attributes(&attributes); - /* Encrypt and authenticate */ - /* PSA AEAD encrypt outputs: ciphertext || tag */ status = psa_aead_encrypt(key_id, alg, - nonce, BLE_EAD_NONCE_SIZE, + nonce, ESP_BLE_EAD_NONCE_SIZE, aad, aad_len, plaintext, plaintext_len, ciphertext, plaintext_len + tag_len, &output_length); if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_aead_encrypt failed: %d", status); + ESP_LOGE(TAG, "psa_aead_encrypt failed: %d", (int)status); psa_destroy_key(key_id); return -1; } @@ -173,47 +161,37 @@ static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, psa_destroy_key(key_id); return 0; -#else - #error "No crypto library selected" #endif } -/** - * @brief AES-CCM decryption with authentication using selected crypto library - */ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, - const uint8_t *ciphertext, size_t ciphertext_len, - const uint8_t *aad, size_t aad_len, - uint8_t *plaintext, size_t tag_len, - size_t plaintext_capacity) + const uint8_t *ciphertext, size_t ciphertext_len, + const uint8_t *aad, size_t aad_len, + uint8_t *plaintext, size_t tag_len, + size_t plaintext_capacity) { #if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) struct tc_aes_key_sched_struct sched; struct tc_ccm_mode_struct ccm_state; int ret; - /* ciphertext_len here includes both ciphertext and tag */ size_t plaintext_len; - /* Validate inputs */ if (key == NULL || nonce == NULL || ciphertext == NULL || plaintext == NULL) { ESP_LOGE(TAG, "Invalid input parameters"); return -1; } - /* Check for integer underflow */ if (ciphertext_len < tag_len) { ESP_LOGE(TAG, "ciphertext_len (%zu) < tag_len (%zu)", ciphertext_len, tag_len); return -1; } plaintext_len = ciphertext_len - tag_len; - if (plaintext_len > plaintext_capacity) { ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); return -1; } - /* Set AES encryption key */ ret = tc_aes128_set_encrypt_key(&sched, key); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_aes128_set_encrypt_key failed"); @@ -221,12 +199,7 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Configure CCM mode */ - ccm_state.sched = &sched; - ccm_state.nonce = (uint8_t *)nonce; - ccm_state.mlen = tag_len; - - ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, BLE_EAD_NONCE_SIZE, tag_len); + ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, ESP_BLE_EAD_NONCE_SIZE, tag_len); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_ccm_config failed"); memset(&sched, 0, sizeof(sched)); @@ -234,12 +207,10 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Decrypt and verify tag */ - /* TinyCrypt expects: ciphertext || tag */ ret = tc_ccm_decryption_verification(plaintext, plaintext_len, - aad, aad_len, - (uint8_t *)ciphertext, ciphertext_len, - &ccm_state); + aad, aad_len, + (uint8_t *)ciphertext, ciphertext_len, + &ccm_state); if (ret != TC_CRYPTO_SUCCESS) { ESP_LOGE(TAG, "tc_ccm_decryption_verification failed"); memset(&sched, 0, sizeof(sched)); @@ -247,7 +218,6 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, return -1; } - /* Clear sensitive data from key schedule */ memset(&sched, 0, sizeof(sched)); memset(&ccm_state, 0, sizeof(ccm_state)); return 0; @@ -258,53 +228,51 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, psa_key_id_t key_id = 0; psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_len); size_t output_length = 0; - /* ciphertext_len here includes both ciphertext and tag */ size_t plaintext_len; - /* Validate inputs */ if (key == NULL || nonce == NULL || ciphertext == NULL || plaintext == NULL) { ESP_LOGE(TAG, "Invalid input parameters"); return -1; } - /* Check for integer underflow */ if (ciphertext_len < tag_len) { ESP_LOGE(TAG, "ciphertext_len (%zu) < tag_len (%zu)", ciphertext_len, tag_len); return -1; } plaintext_len = ciphertext_len - tag_len; - if (plaintext_len > plaintext_capacity) { ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); return -1; } - /* Set key attributes */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + ESP_LOGE(TAG, "psa_crypto_init failed: %d", (int)status); + return -1; + } + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); psa_set_key_algorithm(&attributes, alg); psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attributes, BLE_EAD_KEY_SIZE * 8); + psa_set_key_bits(&attributes, ESP_BLE_EAD_KEY_SIZE * 8); - /* Import key */ - status = psa_import_key(&attributes, key, BLE_EAD_KEY_SIZE, &key_id); + status = psa_import_key(&attributes, key, ESP_BLE_EAD_KEY_SIZE, &key_id); if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_import_key failed: %d", status); + ESP_LOGE(TAG, "psa_import_key failed: %d", (int)status); psa_reset_key_attributes(&attributes); return -1; } psa_reset_key_attributes(&attributes); - /* Decrypt and verify */ - /* PSA AEAD decrypt expects: ciphertext || tag */ status = psa_aead_decrypt(key_id, alg, - nonce, BLE_EAD_NONCE_SIZE, + nonce, ESP_BLE_EAD_NONCE_SIZE, aad, aad_len, ciphertext, ciphertext_len, plaintext, plaintext_len, &output_length); if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_aead_decrypt failed: %d", status); + ESP_LOGE(TAG, "psa_aead_decrypt failed: %d", (int)status); psa_destroy_key(key_id); return -1; } @@ -318,65 +286,74 @@ static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, psa_destroy_key(key_id); return 0; -#else - #error "No crypto library selected" #endif } -int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *payload, size_t payload_size, - uint8_t *encrypted_payload) +esp_err_t esp_ble_ead_encrypt(const uint8_t session_key[ESP_BLE_EAD_KEY_SIZE], + const uint8_t iv[ESP_BLE_EAD_IV_SIZE], + const uint8_t *payload, size_t payload_size, + uint8_t *encrypted_payload) { int ret; - uint8_t nonce[BLE_EAD_NONCE_SIZE]; + uint8_t nonce[ESP_BLE_EAD_NONCE_SIZE]; if (session_key == NULL) { ESP_LOGE(TAG, "session_key is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } if (iv == NULL) { ESP_LOGE(TAG, "iv is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } if (payload == NULL && payload_size > 0) { ESP_LOGE(TAG, "payload is NULL but payload_size > 0"); - return -1; + return ESP_ERR_INVALID_ARG; } if (encrypted_payload == NULL) { ESP_LOGE(TAG, "encrypted_payload is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; + } + + /* CSS Part A 1.23.2: plaintext shall consist of one or more AD structures */ + if (payload_size == 0) { + ESP_LOGE(TAG, "payload_size is 0"); + return ESP_ERR_INVALID_ARG; + } + + if (payload_size > SIZE_MAX - (ESP_BLE_EAD_RANDOMIZER_SIZE + ESP_BLE_EAD_MIC_SIZE)) { + ESP_LOGE(TAG, "payload_size too large"); + return ESP_ERR_INVALID_ARG; } - /* Generate nonce with random randomizer */ ret = ble_ead_generate_nonce(iv, NULL, nonce); if (ret != 0) { - return ret; + return ESP_ERR_INVALID_ARG; } - /* Copy randomizer to the start of encrypted payload */ - memcpy(encrypted_payload, nonce, BLE_EAD_RANDOMIZER_SIZE); + memcpy(encrypted_payload, nonce, ESP_BLE_EAD_RANDOMIZER_SIZE); - /* Encrypt: output = ciphertext + MIC */ ret = ble_aes_ccm_encrypt(session_key, nonce, - payload, payload_size, - ble_ead_aad, BLE_EAD_AAD_SIZE, - &encrypted_payload[BLE_EAD_RANDOMIZER_SIZE], - BLE_EAD_MIC_SIZE); + payload, payload_size, + ble_ead_aad, ESP_BLE_EAD_AAD_SIZE, + &encrypted_payload[ESP_BLE_EAD_RANDOMIZER_SIZE], + ESP_BLE_EAD_MIC_SIZE); + if (ret != 0) { + return ESP_FAIL; + } - return ret; + return ESP_OK; } -int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload, size_t payload_capacity) +esp_err_t esp_ble_ead_decrypt(const uint8_t session_key[ESP_BLE_EAD_KEY_SIZE], + const uint8_t iv[ESP_BLE_EAD_IV_SIZE], + const uint8_t *encrypted_payload, size_t encrypted_payload_size, + uint8_t *payload, size_t payload_capacity) { int ret; - uint8_t nonce[BLE_EAD_NONCE_SIZE]; + uint8_t nonce[ESP_BLE_EAD_NONCE_SIZE]; const uint8_t *randomizer; const uint8_t *ciphertext; size_t ciphertext_len; @@ -384,56 +361,56 @@ int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], if (session_key == NULL) { ESP_LOGE(TAG, "session_key is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } if (iv == NULL) { ESP_LOGE(TAG, "iv is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } if (encrypted_payload == NULL) { ESP_LOGE(TAG, "encrypted_payload is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } if (payload == NULL) { ESP_LOGE(TAG, "payload is NULL"); - return -1; + return ESP_ERR_INVALID_ARG; } - if (encrypted_payload_size < BLE_EAD_RANDOMIZER_SIZE + BLE_EAD_MIC_SIZE) { + /* Randomizer + MIC + at least 1 byte of AD structure */ + if (encrypted_payload_size < ESP_BLE_EAD_RANDOMIZER_SIZE + ESP_BLE_EAD_MIC_SIZE + 1) { ESP_LOGE(TAG, "encrypted_payload_size too small"); - return -1; + return ESP_ERR_INVALID_ARG; } - expected_plaintext_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size); + expected_plaintext_len = ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size); if (expected_plaintext_len > payload_capacity) { ESP_LOGE(TAG, "EAD plaintext length %zu exceeds payload buffer %zu", expected_plaintext_len, payload_capacity); - return -1; + return ESP_ERR_INVALID_ARG; } - /* Extract randomizer from the start of encrypted payload */ randomizer = encrypted_payload; + ciphertext = &encrypted_payload[ESP_BLE_EAD_RANDOMIZER_SIZE]; + ciphertext_len = encrypted_payload_size - ESP_BLE_EAD_RANDOMIZER_SIZE; - /* Ciphertext + MIC follows the randomizer */ - ciphertext = &encrypted_payload[BLE_EAD_RANDOMIZER_SIZE]; - /* ciphertext_len includes both ciphertext and MIC (tag) for PSA API */ - ciphertext_len = encrypted_payload_size - BLE_EAD_RANDOMIZER_SIZE; - - /* Generate nonce from randomizer and IV */ ret = ble_ead_generate_nonce(iv, randomizer, nonce); if (ret != 0) { - return ret; + return ESP_ERR_INVALID_ARG; } - /* Decrypt and verify */ ret = ble_aes_ccm_decrypt(session_key, nonce, - ciphertext, ciphertext_len, - ble_ead_aad, BLE_EAD_AAD_SIZE, - payload, BLE_EAD_MIC_SIZE, - payload_capacity); + ciphertext, ciphertext_len, + ble_ead_aad, ESP_BLE_EAD_AAD_SIZE, + payload, ESP_BLE_EAD_MIC_SIZE, + payload_capacity); + if (ret != 0) { + return ESP_FAIL; + } - return ret; + return ESP_OK; } + +#endif /* CONFIG_BT_BLE_FEAT_ENC_ADV_DATA */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_ble_ead.h b/components/bt/host/bluedroid/api/include/api/esp_ble_ead.h new file mode 100644 index 00000000000..b2fd54045bc --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_ble_ead.h @@ -0,0 +1,122 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __ESP_BLE_EAD_H__ +#define __ESP_BLE_EAD_H__ + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief BLE Encrypted Advertising Data (EAD) + * + * Based on Bluetooth Core Specification Version 5.4 and Core Specification + * Supplement v11, Part A 1.23. + * + * Enable CONFIG_BT_BLE_FEAT_ENC_ADV_DATA to compile the encrypt/decrypt APIs. + * A GATT server that publishes the session key should also enable + * CONFIG_BT_GATTS_KEY_MATERIAL_CHAR and call esp_ble_gap_set_key_material(). + */ + +#define ESP_BLE_EAD_KEY_SIZE 16 /*!< 128-bit session key */ +#define ESP_BLE_EAD_IV_SIZE 8 /*!< 64-bit Initialization Vector */ +#define ESP_BLE_EAD_RANDOMIZER_SIZE 5 /*!< 40-bit Randomizer */ +#define ESP_BLE_EAD_MIC_SIZE 4 /*!< 32-bit Message Integrity Check */ +#define ESP_BLE_EAD_NONCE_SIZE 13 /*!< 104-bit Nonce (Randomizer + IV) */ +#define ESP_BLE_EAD_AAD_SIZE 1 /*!< Additional Authenticated Data size */ + +/** + * Direction bit position in Randomizer (MSB of last byte). + * Per Bluetooth Core Spec Supplement v11, Part A 1.23.3. + */ +#define ESP_BLE_EAD_RANDOMIZER_DIRECTION_BIT 7 + +/** + * @brief Calculate encrypted payload size from plaintext size + * + * Encrypted payload layout: Randomizer || Ciphertext || MIC + */ +#define ESP_BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) \ + (ESP_BLE_EAD_RANDOMIZER_SIZE + (payload_size) + ESP_BLE_EAD_MIC_SIZE) + +/** + * @brief Calculate decrypted payload size from encrypted payload size + */ +#define ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_size) \ + ((encrypted_size) - ESP_BLE_EAD_RANDOMIZER_SIZE - ESP_BLE_EAD_MIC_SIZE) + +/** + * @brief Key material structure for EAD + */ +typedef struct { + uint8_t session_key[ESP_BLE_EAD_KEY_SIZE]; /*!< 128-bit session key */ + uint8_t iv[ESP_BLE_EAD_IV_SIZE]; /*!< 64-bit Initialization Vector */ +} esp_ble_ead_key_material_t; + +/** + * @brief Encrypt advertising data using AES-CCM + * + * The resulting data in @p encrypted_payload has the following layout: + * - Randomizer in the first ESP_BLE_EAD_RANDOMIZER_SIZE bytes + * - Encrypted payload of @p payload_size bytes + * - MIC in the last ESP_BLE_EAD_MIC_SIZE bytes + * + * The function must be called each time the RPA is updated or the advertising + * data are modified. @p payload may contain one or more concatenated advertising + * structures (length + type + data). + * + * @param[in] session_key 16-byte session key + * @param[in] iv 8-byte Initialization Vector. Must be changed + * each time the session key changes + * @param[in] payload Plaintext advertising data to encrypt + * @param[in] payload_size Size of plaintext data. Must be greater than 0 + * @param[out] encrypted_payload Output buffer for encrypted data. + * Size must be at least ESP_BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_ARG: invalid argument + * - ESP_FAIL: encryption failed + */ +esp_err_t esp_ble_ead_encrypt(const uint8_t session_key[ESP_BLE_EAD_KEY_SIZE], + const uint8_t iv[ESP_BLE_EAD_IV_SIZE], + const uint8_t *payload, size_t payload_size, + uint8_t *encrypted_payload); + +/** + * @brief Decrypt advertising data using AES-CCM + * + * @param[in] session_key 16-byte session key + * @param[in] iv 8-byte Initialization Vector + * @param[in] encrypted_payload Encrypted advertising data (includes randomizer and MIC). + * This should only contain the advertising data from the + * received advertising structure, neither the length nor the type + * @param[in] encrypted_payload_size Size of encrypted data + * @param[out] payload Output buffer for decrypted data. + * Use ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE to get the right size + * @param[in] payload_capacity Size of @p payload in bytes; must be >= + * ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) + * + * @return + * - ESP_OK: success + * - ESP_ERR_INVALID_ARG: invalid argument + * - ESP_FAIL: decryption or authentication failed + */ +esp_err_t esp_ble_ead_decrypt(const uint8_t session_key[ESP_BLE_EAD_KEY_SIZE], + const uint8_t iv[ESP_BLE_EAD_IV_SIZE], + const uint8_t *encrypted_payload, size_t encrypted_payload_size, + uint8_t *payload, size_t payload_capacity); + +#ifdef __cplusplus +} +#endif + +#endif /* __ESP_BLE_EAD_H__ */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h index 807b8ebfe80..b4bfe473705 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_gap_ble_api.h @@ -364,6 +364,7 @@ typedef enum { ESP_BLE_AD_TYPE_TRANS_DISC_DATA = 0x26, /* relate to BTM_BLE_AD_TYPE_TRANS_DISC_DATA in stack/btm_ble_api.h */ ESP_BLE_AD_TYPE_LE_SUPPORT_FEATURE = 0x27, /* relate to BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE in stack/btm_ble_api.h */ ESP_BLE_AD_TYPE_CHAN_MAP_UPDATE = 0x28, /* relate to BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE in stack/btm_ble_api.h */ + ESP_BLE_AD_TYPE_ENC_ADV_DATA = 0x31, /* relate to BTM_BLE_AD_TYPE_ENC_ADV_DATA in stack/btm_ble_api.h */ ESP_BLE_AD_MANUFACTURER_SPECIFIC_TYPE = 0xFF, /* relate to BTM_BLE_AD_MANUFACTURER_SPECIFIC_TYPE in stack/btm_ble_api.h */ } esp_ble_adv_data_type; @@ -3584,7 +3585,10 @@ esp_err_t esp_ble_gap_get_device_name(void); * * This function sets the session key and IV that will be exposed * through the Key Material characteristic (UUID 0x2B88) in the GAP service. - * The Key Material allows central devices to decrypt encrypted advertising data. + * A central can read the characteristic over an encrypted GATT link and + * decrypt Encrypted Advertising Data with esp_ble_ead_decrypt(). + * Enabling this API also requires CONFIG_BT_GATTS_KEY_MATERIAL_CHAR, + * which selects CONFIG_BT_BLE_FEAT_ENC_ADV_DATA. * * @param[in] session_key - 16-byte (128-bit) session key for AES-CCM encryption * @param[in] iv - 8-byte (64-bit) initialization vector @@ -3593,6 +3597,7 @@ esp_err_t esp_ble_gap_get_device_name(void); * - ESP_OK : success * - other : failed * + * @see esp_ble_ead_encrypt, esp_ble_ead_decrypt */ esp_err_t esp_ble_gap_set_key_material(const uint8_t session_key[16], const uint8_t iv[8]); #endif // CONFIG_BT_GATTS_KEY_MATERIAL_CHAR diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h index e3a408bd6cf..0b91148ecde 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_ble_api.h @@ -359,6 +359,7 @@ typedef UINT32 tBTM_BLE_AD_MASK; #define BTM_BLE_AD_TYPE_TRANS_DISC_DATA 0x26 #define BTM_BLE_AD_TYPE_LE_SUPPORT_FEATURE 0x27 #define BTM_BLE_AD_TYPE_CHAN_MAP_UPDATE 0x28 +#define BTM_BLE_AD_TYPE_ENC_ADV_DATA 0x31 #define BTM_BLE_AD_TYPE_MANU HCI_EIR_MANUFACTURER_SPECIFIC_TYPE /* 0xff */ typedef UINT8 tBTM_BLE_AD_TYPE; diff --git a/docs/conf_common.py b/docs/conf_common.py index 676fb0aa3a2..7b97b603331 100644 --- a/docs/conf_common.py +++ b/docs/conf_common.py @@ -47,6 +47,7 @@ BLE_DOCS = [ 'api-guides/low-power-mode/low-power-mode-ble.rst', 'api-reference/bluetooth/bt_le.rst', 'api-reference/bluetooth/esp_gap_ble.rst', + 'api-reference/bluetooth/esp_ble_ead.rst', 'api-reference/bluetooth/esp_gatt_defs.rst', 'api-reference/bluetooth/esp_gatts.rst', 'api-reference/bluetooth/esp_gattc.rst', diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile index e92f3482276..11a2b563570 100644 --- a/docs/doxygen/Doxyfile +++ b/docs/doxygen/Doxyfile @@ -85,6 +85,7 @@ INPUT = \ $(PROJECT_PATH)/components/bt/esp_ble_iso/api/include/esp_ble_iso_common_api.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_a2dp_api.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_avrc_api.h \ + $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_ble_ead.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_bt_defs.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_bt_device.h \ $(PROJECT_PATH)/components/bt/host/bluedroid/api/include/api/esp_bt_main.h \ diff --git a/docs/en/api-reference/bluetooth/bt_le.rst b/docs/en/api-reference/bluetooth/bt_le.rst index bba51ce5bc6..fa4caf869bf 100644 --- a/docs/en/api-reference/bluetooth/bt_le.rst +++ b/docs/en/api-reference/bluetooth/bt_le.rst @@ -25,6 +25,10 @@ The Bluetooth LE API in ESP-IDF is organized into the following parts: Discovers and accesses services on remote servers (central role) +- :doc:`Bluetooth Low Energy Encrypted Advertising Data ` + + Encrypts and decrypts advertising payloads with AES-CCM (Bluetooth Core Specification 5.4) + .. only:: SOC_BLUFI_SUPPORTED - :doc:`Bluetooth Low Energy BluFi ` @@ -41,4 +45,5 @@ Each part typically includes an **Overview**, **Application Examples**, and **AP Bluetooth Low Energy GATT Define Bluetooth Low Energy GATT Server Bluetooth Low Energy GATT Client + Bluetooth Low Energy Encrypted Advertising Data :SOC_BLUFI_SUPPORTED: Bluetooth Low Energy BluFi diff --git a/docs/en/api-reference/bluetooth/esp_ble_ead.rst b/docs/en/api-reference/bluetooth/esp_ble_ead.rst new file mode 100644 index 00000000000..0a671baf771 --- /dev/null +++ b/docs/en/api-reference/bluetooth/esp_ble_ead.rst @@ -0,0 +1,31 @@ +Encrypted Advertising Data (EAD) +================================ + +:link_to_translation:`zh_CN:[中文]` + +Overview +-------- + +Encrypted Advertising Data (EAD) was introduced in Bluetooth Core Specification 5.4. It allows a device to encrypt one or more advertising structures with AES-CCM, so that only peers that hold the corresponding session key and IV can recover the plaintext. + +The Bluedroid host exposes this as a pair of synchronous APIs in ``esp_ble_ead.h``. Encryption and decryption are performed in the host and do not require a controller feature bit. + +These APIs are compiled when ``CONFIG_BT_BLE_FEAT_ENC_ADV_DATA`` is enabled. + +The GAP Key Material characteristic (UUID 0x2B88, ``CONFIG_BT_GATTS_KEY_MATERIAL_CHAR``) is the standard way for a peripheral to publish the session key and IV. Enabling that option also selects the EAD APIs. Call :cpp:func:`esp_ble_gap_set_key_material` to set the value so a peer can read it over an encrypted GATT connection, then decrypt with :cpp:func:`esp_ble_ead_decrypt`. + +A central that already has a pre-shared key only needs ``CONFIG_BT_BLE_FEAT_ENC_ADV_DATA``. + +Application Examples +-------------------- + +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph` demonstrates encrypting advertising data and exposing Key Material through the GAP service. + +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent` demonstrates scanning for encrypted advertising data and decrypting it after reading, or using, the Key Material. + +In menuconfig, see ``Bluedroid Options`` > ``Encrypted Advertising Data (EAD)``. + +API Reference +------------- + +.. include-build-file:: inc/esp_ble_ead.inc diff --git a/docs/en/api-reference/bluetooth/esp_gap_ble.rst b/docs/en/api-reference/bluetooth/esp_gap_ble.rst index 04739f0b15c..802964bba9b 100644 --- a/docs/en/api-reference/bluetooth/esp_gap_ble.rst +++ b/docs/en/api-reference/bluetooth/esp_gap_ble.rst @@ -10,6 +10,8 @@ Application Examples - :example:`bluetooth/bluedroid/ble/gatt_security_server` demonstrates how to use ESP BLE security APIs on {IDF_TARGET_NAME} to establish a secure connection and encrypt communication with peer devices while acting as a GATT server. +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph` demonstrates Encrypted Advertising Data and how to publish Key Material with :cpp:func:`esp_ble_gap_set_key_material`. See also :doc:`esp_ble_ead`. + API Reference ------------- diff --git a/docs/zh_CN/api-reference/bluetooth/bt_le.rst b/docs/zh_CN/api-reference/bluetooth/bt_le.rst index 35d889bcff4..35a535dee0f 100644 --- a/docs/zh_CN/api-reference/bluetooth/bt_le.rst +++ b/docs/zh_CN/api-reference/bluetooth/bt_le.rst @@ -25,6 +25,10 @@ ESP-IDF 中的低功耗蓝牙 API 包括以下部分: 发现并访问远程服务器的服务(中心设备角色) +- :doc:`Bluetooth Low Energy Encrypted Advertising Data ` + + 使用 AES-CCM 加密和解密广播载荷(蓝牙核心规范 5.4) + .. only:: SOC_BLUFI_SUPPORTED - :doc:`Bluetooth Low Energy BluFi ` @@ -41,4 +45,5 @@ ESP-IDF 中的低功耗蓝牙 API 包括以下部分: Bluetooth Low Energy GATT Define Bluetooth Low Energy GATT Server Bluetooth Low Energy GATT Client + Bluetooth Low Energy Encrypted Advertising Data :SOC_BLUFI_SUPPORTED: Bluetooth Low Energy BluFi diff --git a/docs/zh_CN/api-reference/bluetooth/esp_ble_ead.rst b/docs/zh_CN/api-reference/bluetooth/esp_ble_ead.rst new file mode 100644 index 00000000000..2fa5f8c0767 --- /dev/null +++ b/docs/zh_CN/api-reference/bluetooth/esp_ble_ead.rst @@ -0,0 +1,31 @@ +加密广播数据 (EAD) +================== + +:link_to_translation:`en:[English]` + +概述 +-------- + +加密广播数据 (Encrypted Advertising Data,EAD) 引入于蓝牙核心规范 5.4。设备可以使用 AES-CCM 加密一段或多段广播结构,只有持有对应 Session Key 和 IV 的对端才能还原明文。 + +Bluedroid host 在 ``esp_ble_ead.h`` 中提供一组同步 API。加解密在 host 侧完成,不依赖控制器特性位。 + +启用 ``CONFIG_BT_BLE_FEAT_ENC_ADV_DATA`` 后会编译这些 API。 + +GAP Key Material 特征 (UUID 0x2B88,``CONFIG_BT_GATTS_KEY_MATERIAL_CHAR``) 是 Peripheral 发布 Session Key 和 IV 的标准做法。打开该选项会同时选中 EAD 加解密 API。调用 :cpp:func:`esp_ble_gap_set_key_material` 写入特征值后,对端可在加密 GATT 连接上读取,再用 :cpp:func:`esp_ble_ead_decrypt` 解密广播。 + +若 Central 已持有预共享密钥,只需打开 ``CONFIG_BT_BLE_FEAT_ENC_ADV_DATA``。 + +应用示例 +-------------------- + +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph` 演示如何加密广播数据,并通过 GAP 服务提供 Key Material。 + +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent` 演示如何扫描加密广播数据,并在读取或使用 Key Material 后解密。 + +在 menuconfig 中见 **Bluedroid Options → Encrypted Advertising Data (EAD)**。 + +API 参考 +------------- + +.. include-build-file:: inc/esp_ble_ead.inc diff --git a/docs/zh_CN/api-reference/bluetooth/esp_gap_ble.rst b/docs/zh_CN/api-reference/bluetooth/esp_gap_ble.rst index 53a0e24718d..2c3fafdd1fb 100644 --- a/docs/zh_CN/api-reference/bluetooth/esp_gap_ble.rst +++ b/docs/zh_CN/api-reference/bluetooth/esp_gap_ble.rst @@ -10,6 +10,8 @@ GAP API - :example:`bluetooth/bluedroid/ble/gatt_security_server` 演示使用 ESP 低功耗蓝牙 security API,{IDF_TARGET_NAME} 作为 GATT 服务器时如何建立安全连接并加密与对等设备的通信。 +- :example:`bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph` 演示加密广播数据,并通过 :cpp:func:`esp_ble_gap_set_key_material` 发布 Key Material。另见 :doc:`esp_ble_ead`。 + API 参考 ------------- diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/README.md b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/README.md index b1d3450e1bb..7338750dc77 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/README.md +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/README.md @@ -5,6 +5,8 @@ This example demonstrates how to receive and decrypt BLE Encrypted Advertising Data (EAD) with Bluedroid stack. +Decryption uses the host APIs in `esp_ble_ead.h` (`esp_ble_ead_decrypt`). Enable `CONFIG_BT_BLE_FEAT_ENC_ADV_DATA` (already set in `sdkconfig.defaults`). + ## Overview This central example works with the `enc_adv_data_prph` peripheral example to demonstrate: @@ -34,7 +36,7 @@ This central example works with the `enc_adv_data_prph` peripheral example to de │ ▼ │ │ key to decrypt │ │ Store key │ │ │ │ │ │ │ │ ▼ │ -│ ▼ │ │ ✅ Decrypt immediately │ +│ ▼ │ │ Decrypt immediately │ │ Later scans: │ │ │ │ ┌─────────┐ ┌─────────┐ │ │ │ │ Central │──▶│ Periph │ │ │ @@ -42,12 +44,12 @@ This central example works with the `enc_adv_data_prph` peripheral example to de │ │ │ │ │ │ No connection needed │ │ │ ▼ │ │ -│ ✅ Decrypt using stored key │ │ +│ Decrypt using stored key │ │ │ │ │ ├────────────────────────────────┼────────────────────────────────────────────┤ -│ ✓ Secure key exchange │ ✓ No connection latency │ -│ ✓ Dynamic key support │ ✓ Simpler implementation │ -│ ✗ First-time connection needed │ ✗ Key must be pre-provisioned │ +│ + Secure key exchange │ + No connection latency │ +│ + Dynamic key support │ + Simpler implementation │ +│ - First-time connection needed │ - Key must be pre-provisioned │ └────────────────────────────────┴────────────────────────────────────────────┘ ``` @@ -69,7 +71,7 @@ This central example works with the `enc_adv_data_prph` peripheral example to de │ │ 1. Scan │ │ │ │ ──────────────────────────────────────────────────▶ │ │ │ │ │ │ -│ │ 2. Receive Adv (UUID=0x2C01, Encrypted Data) │ │ +│ │ 2. Receive Adv (UUID=0x1800, Encrypted Data) │ │ │ │ ◀────────────────────────────────────────────────── │ │ │ │ │ │ │ │ [No key yet - cannot decrypt] │ │ @@ -100,7 +102,7 @@ This central example works with the `enc_adv_data_prph` peripheral example to de │ │ │ │ │ │ 11. Decrypt using stored key (NO CONNECTION!) │ │ │ │ ┌────────────────────────────────────────┐ │ │ -│ │ │ ble_ead_decrypt(session_key, iv, ...) │ │ │ +│ │ │ esp_ble_ead_decrypt(session_key, iv, ...) │ │ │ │ │ │ Result: "prph" (decrypted name) │ │ │ │ │ └────────────────────────────────────────┘ │ │ │ │ │ │ @@ -136,13 +138,13 @@ This central example works with the `enc_adv_data_prph` peripheral example to de │ │ │ │ │ │ 3. Immediately decrypt (NO CONNECTION!) │ │ │ │ ┌────────────────────────────────────────┐ │ │ -│ │ │ ble_ead_decrypt(pre_shared_key, ...) │ │ │ +│ │ │ esp_ble_ead_decrypt(pre_shared_key, ...) │ │ │ │ │ │ Result: "prph" (decrypted name) │ │ │ │ │ └────────────────────────────────────────┘ │ │ │ │ │ │ │ ▼ ▼ │ │ │ -│ ⚡ No connection overhead - instant decryption! │ +│ No connection overhead - instant decryption! │ │ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` @@ -253,11 +255,11 @@ I (XXX) ENC_ADV_CENT: Decrypted device name: prph I (XXX) ENC_ADV_CENT_SIMPLE: ======================================== I (XXX) ENC_ADV_CENT_SIMPLE: EAD Central - No Connection Mode I (XXX) ENC_ADV_CENT_SIMPLE: ======================================== -I (XXX) ENC_ADV_CENT_SIMPLE: ⚡ This example decrypts WITHOUT connecting! -I (XXX) ENC_ADV_CENT_SIMPLE: 🔍 Scanning started (no connection mode) +I (XXX) ENC_ADV_CENT_SIMPLE: This example decrypts WITHOUT connecting! +I (XXX) ENC_ADV_CENT_SIMPLE: Scanning started (no connection mode) ... -I (XXX) ENC_ADV_CENT_SIMPLE: ✅ Decryption successful (no connection needed!) -I (XXX) ENC_ADV_CENT_SIMPLE: 📛 Decrypted device name: "prph" +I (XXX) ENC_ADV_CENT_SIMPLE: Decryption successful (no connection needed!) +I (XXX) ENC_ADV_CENT_SIMPLE: Decrypted device name: "prph" ``` ## Troubleshooting diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/CMakeLists.txt index 6bf438be392..449200e68e4 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/CMakeLists.txt @@ -4,5 +4,5 @@ else() set(MAIN_SRC "enc_adv_data_cent.c") endif() -idf_component_register(SRCS ${MAIN_SRC} "ble_ead.c" +idf_component_register(SRCS ${MAIN_SRC} INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c deleted file mode 100644 index 65459a03905..00000000000 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.c +++ /dev/null @@ -1,446 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include "ble_ead.h" -#include "esp_random.h" -#include "esp_log.h" -#include "sdkconfig.h" - -#define TAG "BLE_EAD" - -/* Select crypto library based on configuration */ -#if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) -#include "tinycrypt/aes.h" -#include "tinycrypt/ccm_mode.h" -#include "tinycrypt/constants.h" -#elif defined(CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS) -#include "psa/crypto.h" -#else -#error "Please select either CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT or CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS" -#endif - -/* Additional Authenticated Data for EAD - EA (Encrypted Advertising) */ -static const uint8_t ble_ead_aad[BLE_EAD_AAD_SIZE] = { 0xEA }; - -/** - * @brief Generate randomizer with direction bit set - * - * Per Bluetooth Core Spec Supplement v11, Part A 1.23.3: - * The MSB of the Randomizer shall be set to indicate direction - */ -static int ble_ead_generate_randomizer(uint8_t randomizer[BLE_EAD_RANDOMIZER_SIZE]) -{ - /* Generate random bytes */ - esp_fill_random(randomizer, BLE_EAD_RANDOMIZER_SIZE); - - /* Set direction bit (MSB of last byte) - required by spec */ - randomizer[BLE_EAD_RANDOMIZER_SIZE - 1] |= (1 << BLE_EAD_RANDOMIZER_DIRECTION_BIT); - - return 0; -} - -/** - * @brief Generate nonce from IV and randomizer - * - * Nonce = Randomizer (5 bytes) || IV (8 bytes) = 13 bytes - */ -static int ble_ead_generate_nonce(const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t randomizer[BLE_EAD_RANDOMIZER_SIZE], - uint8_t nonce[BLE_EAD_NONCE_SIZE]) -{ - if (iv == NULL || nonce == NULL) { - return -1; - } - - /* Randomizer in first 5 bytes */ - if (randomizer != NULL) { - memcpy(nonce, randomizer, BLE_EAD_RANDOMIZER_SIZE); - } else { - /* Generate new randomizer with direction bit */ - ble_ead_generate_randomizer(nonce); - } - - /* IV in last 8 bytes */ - memcpy(nonce + BLE_EAD_RANDOMIZER_SIZE, iv, BLE_EAD_IV_SIZE); - - return 0; -} - -/** - * @brief AES-CCM encryption using selected crypto library - */ -static int ble_aes_ccm_encrypt(const uint8_t *key, const uint8_t *nonce, - const uint8_t *plaintext, size_t plaintext_len, - const uint8_t *aad, size_t aad_len, - uint8_t *ciphertext, size_t tag_len) -{ -#if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) - struct tc_aes_key_sched_struct sched; - struct tc_ccm_mode_struct ccm_state; - int ret; - - /* Validate inputs */ - if (key == NULL || nonce == NULL || ciphertext == NULL) { - ESP_LOGE(TAG, "Invalid input parameters"); - return -1; - } - - /* Set AES encryption key */ - ret = tc_aes128_set_encrypt_key(&sched, key); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_aes128_set_encrypt_key failed"); - memset(&sched, 0, sizeof(sched)); - return -1; - } - - /* Configure CCM mode */ - ccm_state.sched = &sched; - ccm_state.nonce = (uint8_t *)nonce; - ccm_state.mlen = tag_len; - - ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, BLE_EAD_NONCE_SIZE, tag_len); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_ccm_config failed"); - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return -1; - } - - /* Encrypt and generate tag */ - /* TinyCrypt outputs: ciphertext || tag */ - ret = tc_ccm_generation_encryption(ciphertext, plaintext_len + tag_len, - aad, aad_len, - plaintext, plaintext_len, - &ccm_state); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_ccm_generation_encryption failed"); - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return -1; - } - - /* Clear sensitive data from key schedule */ - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return 0; - -#elif defined(CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS) - psa_status_t status; - psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t key_id = 0; - psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_len); - size_t output_length = 0; - - /* Validate inputs */ - if (key == NULL || nonce == NULL || ciphertext == NULL) { - ESP_LOGE(TAG, "Invalid input parameters"); - return -1; - } - - /* Set key attributes */ - psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&attributes, alg); - psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attributes, BLE_EAD_KEY_SIZE * 8); - - /* Import key */ - status = psa_import_key(&attributes, key, BLE_EAD_KEY_SIZE, &key_id); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_import_key failed: %d", status); - psa_reset_key_attributes(&attributes); - return -1; - } - psa_reset_key_attributes(&attributes); - - /* Encrypt and authenticate */ - /* PSA AEAD encrypt outputs: ciphertext || tag */ - status = psa_aead_encrypt(key_id, alg, - nonce, BLE_EAD_NONCE_SIZE, - aad, aad_len, - plaintext, plaintext_len, - ciphertext, plaintext_len + tag_len, - &output_length); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_aead_encrypt failed: %d", status); - psa_destroy_key(key_id); - return -1; - } - - if (output_length != plaintext_len + tag_len) { - ESP_LOGE(TAG, "psa_aead_encrypt output length mismatch: expected %zu, got %zu", - plaintext_len + tag_len, output_length); - psa_destroy_key(key_id); - return -1; - } - - psa_destroy_key(key_id); - return 0; -#else - #error "No crypto library selected" -#endif -} - -/** - * @brief AES-CCM decryption with authentication using selected crypto library - */ -static int ble_aes_ccm_decrypt(const uint8_t *key, const uint8_t *nonce, - const uint8_t *ciphertext, size_t ciphertext_len, - const uint8_t *aad, size_t aad_len, - uint8_t *plaintext, size_t tag_len, - size_t plaintext_capacity) -{ -#if defined(CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT) - struct tc_aes_key_sched_struct sched; - struct tc_ccm_mode_struct ccm_state; - int ret; - /* ciphertext_len here includes both ciphertext and tag */ - size_t plaintext_len; - - /* Validate inputs */ - if (key == NULL || nonce == NULL || ciphertext == NULL || plaintext == NULL) { - ESP_LOGE(TAG, "Invalid input parameters"); - return -1; - } - - /* Check for integer underflow */ - if (ciphertext_len < tag_len) { - ESP_LOGE(TAG, "ciphertext_len (%zu) < tag_len (%zu)", ciphertext_len, tag_len); - return -1; - } - - plaintext_len = ciphertext_len - tag_len; - - if (plaintext_len > plaintext_capacity) { - ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); - return -1; - } - - /* Set AES encryption key */ - ret = tc_aes128_set_encrypt_key(&sched, key); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_aes128_set_encrypt_key failed"); - memset(&sched, 0, sizeof(sched)); - return -1; - } - - /* Configure CCM mode */ - ccm_state.sched = &sched; - ccm_state.nonce = (uint8_t *)nonce; - ccm_state.mlen = tag_len; - - ret = tc_ccm_config(&ccm_state, &sched, (uint8_t *)nonce, BLE_EAD_NONCE_SIZE, tag_len); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_ccm_config failed"); - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return -1; - } - - /* Decrypt and verify tag */ - /* TinyCrypt expects: ciphertext || tag */ - ret = tc_ccm_decryption_verification(plaintext, plaintext_len, - aad, aad_len, - (uint8_t *)ciphertext, ciphertext_len, - &ccm_state); - if (ret != TC_CRYPTO_SUCCESS) { - ESP_LOGE(TAG, "tc_ccm_decryption_verification failed"); - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return -1; - } - - /* Clear sensitive data from key schedule */ - memset(&sched, 0, sizeof(sched)); - memset(&ccm_state, 0, sizeof(ccm_state)); - return 0; - -#elif defined(CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS) - psa_status_t status; - psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t key_id = 0; - psa_algorithm_t alg = PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_CCM, tag_len); - size_t output_length = 0; - /* ciphertext_len here includes both ciphertext and tag */ - size_t plaintext_len; - - /* Validate inputs */ - if (key == NULL || nonce == NULL || ciphertext == NULL || plaintext == NULL) { - ESP_LOGE(TAG, "Invalid input parameters"); - return -1; - } - - /* Check for integer underflow */ - if (ciphertext_len < tag_len) { - ESP_LOGE(TAG, "ciphertext_len (%zu) < tag_len (%zu)", ciphertext_len, tag_len); - return -1; - } - - plaintext_len = ciphertext_len - tag_len; - - if (plaintext_len > plaintext_capacity) { - ESP_LOGE(TAG, "plaintext_len (%zu) > plaintext_capacity (%zu)", plaintext_len, plaintext_capacity); - return -1; - } - - /* Set key attributes */ - psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&attributes, alg); - psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); - psa_set_key_bits(&attributes, BLE_EAD_KEY_SIZE * 8); - - /* Import key */ - status = psa_import_key(&attributes, key, BLE_EAD_KEY_SIZE, &key_id); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_import_key failed: %d", status); - psa_reset_key_attributes(&attributes); - return -1; - } - psa_reset_key_attributes(&attributes); - - /* Decrypt and verify */ - /* PSA AEAD decrypt expects: ciphertext || tag */ - /* ciphertext_len here already includes tag length */ - status = psa_aead_decrypt(key_id, alg, - nonce, BLE_EAD_NONCE_SIZE, - aad, aad_len, - ciphertext, ciphertext_len, - plaintext, plaintext_len, - &output_length); - if (status != PSA_SUCCESS) { - ESP_LOGE(TAG, "psa_aead_decrypt failed: %d", status); - psa_destroy_key(key_id); - return -1; - } - - if (output_length != plaintext_len) { - ESP_LOGE(TAG, "psa_aead_decrypt output length mismatch: expected %zu, got %zu", - plaintext_len, output_length); - psa_destroy_key(key_id); - return -1; - } - - psa_destroy_key(key_id); - return 0; -#else - #error "No crypto library selected" -#endif -} - -int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *payload, size_t payload_size, - uint8_t *encrypted_payload) -{ - int ret; - uint8_t nonce[BLE_EAD_NONCE_SIZE]; - - if (session_key == NULL) { - ESP_LOGE(TAG, "session_key is NULL"); - return -1; - } - - if (iv == NULL) { - ESP_LOGE(TAG, "iv is NULL"); - return -1; - } - - if (payload == NULL && payload_size > 0) { - ESP_LOGE(TAG, "payload is NULL but payload_size > 0"); - return -1; - } - - if (encrypted_payload == NULL) { - ESP_LOGE(TAG, "encrypted_payload is NULL"); - return -1; - } - - /* Generate nonce with random randomizer */ - ret = ble_ead_generate_nonce(iv, NULL, nonce); - if (ret != 0) { - return ret; - } - - /* Copy randomizer to the start of encrypted payload */ - memcpy(encrypted_payload, nonce, BLE_EAD_RANDOMIZER_SIZE); - - /* Encrypt: output = ciphertext + MIC */ - ret = ble_aes_ccm_encrypt(session_key, nonce, - payload, payload_size, - ble_ead_aad, BLE_EAD_AAD_SIZE, - &encrypted_payload[BLE_EAD_RANDOMIZER_SIZE], - BLE_EAD_MIC_SIZE); - - return ret; -} - -int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload, size_t payload_capacity) -{ - int ret; - uint8_t nonce[BLE_EAD_NONCE_SIZE]; - const uint8_t *randomizer; - const uint8_t *ciphertext; - size_t ciphertext_len; - size_t expected_plaintext_len; - - if (session_key == NULL) { - ESP_LOGE(TAG, "session_key is NULL"); - return -1; - } - - if (iv == NULL) { - ESP_LOGE(TAG, "iv is NULL"); - return -1; - } - - if (encrypted_payload == NULL) { - ESP_LOGE(TAG, "encrypted_payload is NULL"); - return -1; - } - - if (payload == NULL) { - ESP_LOGE(TAG, "payload is NULL"); - return -1; - } - - if (encrypted_payload_size < BLE_EAD_RANDOMIZER_SIZE + BLE_EAD_MIC_SIZE) { - ESP_LOGE(TAG, "encrypted_payload_size too small"); - return -1; - } - - expected_plaintext_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size); - if (expected_plaintext_len > payload_capacity) { - ESP_LOGE(TAG, "EAD plaintext length %zu exceeds payload buffer %zu", - expected_plaintext_len, payload_capacity); - return -1; - } - - /* Extract randomizer from the start of encrypted payload */ - randomizer = encrypted_payload; - - /* Ciphertext + MIC follows the randomizer */ - ciphertext = &encrypted_payload[BLE_EAD_RANDOMIZER_SIZE]; - /* ciphertext_len includes both ciphertext and MIC (tag) for PSA API */ - ciphertext_len = encrypted_payload_size - BLE_EAD_RANDOMIZER_SIZE; - - /* Generate nonce from randomizer and IV */ - ret = ble_ead_generate_nonce(iv, randomizer, nonce); - if (ret != 0) { - return ret; - } - - /* Decrypt and verify */ - ret = ble_aes_ccm_decrypt(session_key, nonce, - ciphertext, ciphertext_len, - ble_ead_aad, BLE_EAD_AAD_SIZE, - payload, BLE_EAD_MIC_SIZE, - payload_capacity); - - return ret; -} diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h deleted file mode 100644 index 4994f6b994f..00000000000 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/ble_ead.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef BLE_EAD_H -#define BLE_EAD_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief BLE Encrypted Advertising Data (EAD) definitions - * Based on Bluetooth Core Specification Version 5.4 - */ - -#define BLE_EAD_KEY_SIZE 16 /* 128-bit session key */ -#define BLE_EAD_IV_SIZE 8 /* 64-bit Initialization Vector */ -#define BLE_EAD_RANDOMIZER_SIZE 5 /* 40-bit Randomizer */ -#define BLE_EAD_MIC_SIZE 4 /* 32-bit Message Integrity Check */ -#define BLE_EAD_NONCE_SIZE 13 /* 104-bit Nonce (Randomizer + IV) */ -#define BLE_EAD_AAD_SIZE 1 /* Additional Authenticated Data size */ - -/* Direction bit position in Randomizer (MSB of last byte) - * Per Bluetooth Core Spec Supplement v11, Part A 1.23.3 - */ -#define BLE_EAD_RANDOMIZER_DIRECTION_BIT 7 - -/* AD Type for Encrypted Advertising Data (0x31) */ -#define ESP_BLE_AD_TYPE_ENC_ADV_DATA 0x31 - -/** - * @brief Calculate encrypted payload size from plaintext size - */ -#define BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) \ - (BLE_EAD_RANDOMIZER_SIZE + (payload_size) + BLE_EAD_MIC_SIZE) - -/** - * @brief Calculate decrypted payload size from encrypted payload size - */ -#define BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_size) \ - ((encrypted_size) - BLE_EAD_RANDOMIZER_SIZE - BLE_EAD_MIC_SIZE) - -/** - * @brief Key material structure for EAD - */ -typedef struct { - uint8_t session_key[BLE_EAD_KEY_SIZE]; /* 128-bit session key */ - uint8_t iv[BLE_EAD_IV_SIZE]; /* 64-bit Initialization Vector */ -} ble_ead_key_material_t; - -/** - * @brief Encrypt advertising data using AES-CCM - * - * @param session_key 16-byte session key - * @param iv 8-byte Initialization Vector - * @param payload Plaintext advertising data to encrypt - * @param payload_size Size of plaintext data - * @param encrypted_payload Output buffer for encrypted data - * Size must be at least BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) - * - * @return 0 on success, negative error code on failure - */ -int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *payload, size_t payload_size, - uint8_t *encrypted_payload); - -/** - * @brief Decrypt advertising data using AES-CCM - * - * @param session_key 16-byte session key - * @param iv 8-byte Initialization Vector - * @param encrypted_payload Encrypted advertising data (includes randomizer and MIC) - * @param encrypted_payload_size Size of encrypted data - * @param payload Output buffer for decrypted data - * @param payload_capacity Size of @a payload in bytes; must be >= - * BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) - * - * @return 0 on success, negative error code on failure - */ -int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload, size_t payload_capacity); - -#ifdef __cplusplus -} -#endif - -#endif /* BLE_EAD_H */ diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c index 009346703db..8a9029cfa78 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent.c @@ -30,7 +30,7 @@ #include "esp_gatt_common_api.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" -#include "ble_ead.h" +#include "esp_ble_ead.h" #define TAG "ENC_ADV_CENT" @@ -51,7 +51,7 @@ typedef struct { bool valid; esp_bd_addr_t addr; bool key_material_exist; - ble_ead_key_material_t key_material; + esp_ble_ead_key_material_t key_material; } peer_info_t; static peer_info_t peers[MAX_PEERS] = {0}; @@ -142,26 +142,26 @@ static void decrypt_enc_adv_data(const uint8_t *adv_data, uint8_t adv_len, const const uint8_t *enc_data = &adv_data[offset + 2]; uint8_t enc_data_len = len - 1; /* Exclude type byte */ - if (enc_data_len < BLE_EAD_RANDOMIZER_SIZE + BLE_EAD_MIC_SIZE) { + if (enc_data_len < ESP_BLE_EAD_RANDOMIZER_SIZE + ESP_BLE_EAD_MIC_SIZE) { ESP_LOGW(TAG, "Encrypted data too short"); break; } uint8_t dec_data[32]; /* Buffer for decrypted data */ - size_t dec_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); + size_t dec_len = ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); if (dec_len > sizeof(dec_data)) { ESP_LOGW(TAG, "Encrypted AD would yield %zu plaintext bytes; example buffer is %zu — skip", dec_len, sizeof(dec_data)); break; } - int rc = ble_ead_decrypt( + esp_err_t rc = esp_ble_ead_decrypt( peers[peer_idx].key_material.session_key, peers[peer_idx].key_material.iv, enc_data, enc_data_len, dec_data, sizeof(dec_data)); - if (rc == 0) { + if (rc == ESP_OK) { size_t safe_dec_len = dec_len; if (safe_dec_len > sizeof(dec_data)) { ESP_LOGW(TAG, "dec_len %zu > buffer %zu, clamping for log/parse", @@ -448,17 +448,17 @@ static void gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_ param->read.handle, param->read.value_len); if (param->read.handle == key_material_char_handle && - param->read.value_len == sizeof(ble_ead_key_material_t)) { + param->read.value_len == sizeof(esp_ble_ead_key_material_t)) { /* Store key material */ int peer_idx = find_peer(gattc_remote_bda); if (peer_idx >= 0) { memcpy(&peers[peer_idx].key_material, param->read.value, - sizeof(ble_ead_key_material_t)); + sizeof(esp_ble_ead_key_material_t)); peers[peer_idx].key_material_exist = true; ESP_LOGI(TAG, "Key material received:"); ESP_LOG_BUFFER_HEX(TAG, &peers[peer_idx].key_material, - sizeof(ble_ead_key_material_t)); + sizeof(esp_ble_ead_key_material_t)); } /* Disconnect and resume scanning */ diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c index 7fd45def779..30aad8059ea 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/main/enc_adv_data_cent_no_connect.c @@ -24,18 +24,18 @@ #include "esp_bt_main.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" -#include "ble_ead.h" +#include "esp_ble_ead.h" #define TAG "ENC_ADV_CENT_SIMPLE" -/* Custom service UUID to identify target device */ -#define CUSTOM_SERVICE_UUID 0x2C01 +/* GAP Service UUID advertised by enc_adv_data_prph (Key Material lives in GAP) */ +#define GAP_SERVICE_UUID 0x1800 /* * Pre-shared Key Material - MUST match the Peripheral! * In real applications, this would be provisioned securely. */ -static const ble_ead_key_material_t pre_shared_key = { +static const esp_ble_ead_key_material_t pre_shared_key = { .session_key = { 0x19, 0x6a, 0x0a, 0xd1, 0x2a, 0x61, 0x20, 0x1e, 0x13, 0x6e, 0x2e, 0xd1, 0x12, 0xda, 0xa9, 0x57 @@ -72,7 +72,7 @@ static bool is_target_device(const uint8_t *adv_data, uint8_t adv_len) if (payload_len >= 2) { for (int i = 0; i + 1 < payload_len; i += 2) { uint16_t uuid = adv_data[offset + 2 + i] | (adv_data[offset + 3 + i] << 8); - if (uuid == CUSTOM_SERVICE_UUID) { + if (uuid == GAP_SERVICE_UUID) { return true; } } @@ -112,34 +112,34 @@ static void decrypt_adv_data_no_connect(const uint8_t *adv_data, uint8_t adv_len ESP_LOGI(TAG, "Found encrypted advertising data (%d bytes)", enc_data_len); ESP_LOG_BUFFER_HEX(TAG, enc_data, enc_data_len); - if (enc_data_len < BLE_EAD_RANDOMIZER_SIZE + BLE_EAD_MIC_SIZE) { + if (enc_data_len < ESP_BLE_EAD_RANDOMIZER_SIZE + ESP_BLE_EAD_MIC_SIZE) { ESP_LOGW(TAG, "Encrypted data too short"); break; } /* Decrypt using pre-shared key */ uint8_t dec_data[32]; - size_t dec_len = BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); + size_t dec_len = ESP_BLE_EAD_DECRYPTED_PAYLOAD_SIZE(enc_data_len); if (dec_len > sizeof(dec_data)) { ESP_LOGW(TAG, "Encrypted AD would yield %zu plaintext bytes; example buffer is %zu — skip", dec_len, sizeof(dec_data)); return; } - int rc = ble_ead_decrypt( + esp_err_t rc = esp_ble_ead_decrypt( pre_shared_key.session_key, pre_shared_key.iv, enc_data, enc_data_len, dec_data, sizeof(dec_data)); - if (rc == 0) { + if (rc == ESP_OK) { size_t safe_dec_len = dec_len; if (safe_dec_len > sizeof(dec_data)) { ESP_LOGW(TAG, "dec_len %zu > buffer %zu, clamping for log/parse", dec_len, sizeof(dec_data)); safe_dec_len = sizeof(dec_data); } - ESP_LOGI(TAG, "✅ Decryption successful (no connection needed!)"); + ESP_LOGI(TAG, "Decryption successful (no connection needed!)"); ESP_LOGI(TAG, "Decrypted data (%zu bytes):", safe_dec_len); ESP_LOG_BUFFER_HEX(TAG, dec_data, safe_dec_len); @@ -164,14 +164,14 @@ static void decrypt_adv_data_no_connect(const uint8_t *adv_data, uint8_t adv_len name_copy_end <= sizeof(dec_data) && name_copy_end <= safe_dec_len) { memcpy(name, &dec_data[2], name_len); - ESP_LOGI(TAG, "📛 Decrypted device name: \"%s\"", name); + ESP_LOGI(TAG, "Decrypted device name: \"%s\"", name); } } } } } } else { - ESP_LOGE(TAG, "❌ Decryption failed (rc=%d) - wrong key?", rc); + ESP_LOGE(TAG, "Decryption failed (rc=%d) - wrong key?", rc); } return; /* Found and processed encrypted data */ } @@ -194,8 +194,8 @@ static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT: if (param->scan_start_cmpl.status == ESP_BT_STATUS_SUCCESS) { - ESP_LOGI(TAG, "🔍 Scanning started (no connection mode)"); - ESP_LOGI(TAG, "Looking for devices with UUID 0x%04X...", CUSTOM_SERVICE_UUID); + ESP_LOGI(TAG, "Scanning started (no connection mode)"); + ESP_LOGI(TAG, "Looking for devices with UUID 0x%04X...", GAP_SERVICE_UUID); } else { ESP_LOGE(TAG, "Scan start failed: %d", param->scan_start_cmpl.status); } @@ -253,14 +253,14 @@ void app_main(void) /* Display pre-shared key */ ESP_LOGI(TAG, "Using pre-shared key material:"); ESP_LOGI(TAG, " Session Key:"); - ESP_LOG_BUFFER_HEX(TAG, pre_shared_key.session_key, BLE_EAD_KEY_SIZE); + ESP_LOG_BUFFER_HEX(TAG, pre_shared_key.session_key, ESP_BLE_EAD_KEY_SIZE); ESP_LOGI(TAG, " IV:"); - ESP_LOG_BUFFER_HEX(TAG, pre_shared_key.iv, BLE_EAD_IV_SIZE); + ESP_LOG_BUFFER_HEX(TAG, pre_shared_key.iv, ESP_BLE_EAD_IV_SIZE); /* Start scanning */ ESP_ERROR_CHECK(esp_ble_gap_set_scan_params(&ble_scan_params)); ESP_LOGI(TAG, ""); - ESP_LOGI(TAG, "⚡ This example decrypts WITHOUT connecting!"); - ESP_LOGI(TAG, " Key must be pre-shared with peripheral."); + ESP_LOGI(TAG, "This example decrypts WITHOUT connecting!"); + ESP_LOGI(TAG, "Key must be pre-shared with peripheral."); } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults index 7b5d67ee671..a0c67063ef0 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent/sdkconfig.defaults @@ -8,6 +8,9 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # Enable SMP for security CONFIG_BT_BLE_SMP_ENABLE=y +# Encrypted Advertising Data APIs in Bluedroid host +CONFIG_BT_BLE_FEAT_ENC_ADV_DATA=y + # Select crypto library for EAD (Encrypted Advertising Data) # Options: CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT or CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/README.md b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/README.md index d59fbb7626f..2d6078df66c 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/README.md +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/README.md @@ -5,6 +5,8 @@ This example demonstrates how to use BLE Encrypted Advertising Data (EAD) feature with Bluedroid stack. +Encryption uses the host APIs in `esp_ble_ead.h` (`esp_ble_ead_encrypt`). Enable `CONFIG_BT_BLE_FEAT_ENC_ADV_DATA` (already set in `sdkconfig.defaults`). + ## Overview The Encrypted Advertising Data feature (introduced in Bluetooth Core Specification 5.4) allows devices to encrypt portions of their advertising data using AES-CCM. This enables: @@ -30,7 +32,7 @@ The Encrypted Advertising Data feature (introduced in Bluetooth Core Specificati │ ┌──────────────────────────────────────────────────────────────────────┐ │ │ │ BLE Advertising Packet │ │ │ ├──────────┬─────────────┬────────────────┬────────────────────────────┤ │ -│ │ Flags │ Name "key" │ UUID 0x2C01 │ Encrypted Data (AD 0x31) │ │ +│ │ Flags │ Name "key" │ UUID 0x1800 │ Encrypted Data (AD 0x31) │ │ │ │ (3B) │ (5B) │ (4B) │ (16B) │ │ │ └──────────┴─────────────┴────────────────┴────────────────────────────┘ │ │ │ @@ -89,7 +91,7 @@ Offset Length Type Data Description ────── ────── ──── ──── ─────────── 0 2 0x01 0x06 Flags: LE General Discoverable 3 4 0x09 'k' 'e' 'y' Complete Local Name -8 3 0x03 0x01 0x2C 16-bit Service UUID: 0x2C01 +8 3 0x03 0x00 0x18 16-bit Service UUID: 0x1800 12 16 0x31 [Encrypted Payload] Encrypted Advertising Data Encrypted Payload Detail: diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/CMakeLists.txt b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/CMakeLists.txt index a9fa58c00e4..360259dd4e4 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/CMakeLists.txt +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/CMakeLists.txt @@ -1,2 +1,2 @@ -idf_component_register(SRCS "enc_adv_data_prph.c" "ble_ead.c" +idf_component_register(SRCS "enc_adv_data_prph.c" INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/Kconfig.projbuild b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/Kconfig.projbuild index 305d0bfab64..1f6e4ef34bd 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/Kconfig.projbuild +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/Kconfig.projbuild @@ -2,11 +2,13 @@ menu "Example Configuration" config EXAMPLE_ENABLE_KEY_MATERIAL bool "Enable Key Material characteristic in GAP Service" + depends on BT_GATTS_ENABLE default y select BT_GATTS_KEY_MATERIAL_CHAR help Enable the Key Material characteristic in the built-in GAP service - (UUID 0x1800) using the Bluedroid stack's support for this feature. + (UUID 0x2B88). This also enables Encrypted Advertising Data APIs + (CONFIG_BT_BLE_FEAT_ENC_ADV_DATA). This is the standard-compliant approach as defined in Bluetooth Core Specification Version 5.4. diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h deleted file mode 100644 index 4994f6b994f..00000000000 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/ble_ead.h +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#ifndef BLE_EAD_H -#define BLE_EAD_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief BLE Encrypted Advertising Data (EAD) definitions - * Based on Bluetooth Core Specification Version 5.4 - */ - -#define BLE_EAD_KEY_SIZE 16 /* 128-bit session key */ -#define BLE_EAD_IV_SIZE 8 /* 64-bit Initialization Vector */ -#define BLE_EAD_RANDOMIZER_SIZE 5 /* 40-bit Randomizer */ -#define BLE_EAD_MIC_SIZE 4 /* 32-bit Message Integrity Check */ -#define BLE_EAD_NONCE_SIZE 13 /* 104-bit Nonce (Randomizer + IV) */ -#define BLE_EAD_AAD_SIZE 1 /* Additional Authenticated Data size */ - -/* Direction bit position in Randomizer (MSB of last byte) - * Per Bluetooth Core Spec Supplement v11, Part A 1.23.3 - */ -#define BLE_EAD_RANDOMIZER_DIRECTION_BIT 7 - -/* AD Type for Encrypted Advertising Data (0x31) */ -#define ESP_BLE_AD_TYPE_ENC_ADV_DATA 0x31 - -/** - * @brief Calculate encrypted payload size from plaintext size - */ -#define BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) \ - (BLE_EAD_RANDOMIZER_SIZE + (payload_size) + BLE_EAD_MIC_SIZE) - -/** - * @brief Calculate decrypted payload size from encrypted payload size - */ -#define BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_size) \ - ((encrypted_size) - BLE_EAD_RANDOMIZER_SIZE - BLE_EAD_MIC_SIZE) - -/** - * @brief Key material structure for EAD - */ -typedef struct { - uint8_t session_key[BLE_EAD_KEY_SIZE]; /* 128-bit session key */ - uint8_t iv[BLE_EAD_IV_SIZE]; /* 64-bit Initialization Vector */ -} ble_ead_key_material_t; - -/** - * @brief Encrypt advertising data using AES-CCM - * - * @param session_key 16-byte session key - * @param iv 8-byte Initialization Vector - * @param payload Plaintext advertising data to encrypt - * @param payload_size Size of plaintext data - * @param encrypted_payload Output buffer for encrypted data - * Size must be at least BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(payload_size) - * - * @return 0 on success, negative error code on failure - */ -int ble_ead_encrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *payload, size_t payload_size, - uint8_t *encrypted_payload); - -/** - * @brief Decrypt advertising data using AES-CCM - * - * @param session_key 16-byte session key - * @param iv 8-byte Initialization Vector - * @param encrypted_payload Encrypted advertising data (includes randomizer and MIC) - * @param encrypted_payload_size Size of encrypted data - * @param payload Output buffer for decrypted data - * @param payload_capacity Size of @a payload in bytes; must be >= - * BLE_EAD_DECRYPTED_PAYLOAD_SIZE(encrypted_payload_size) - * - * @return 0 on success, negative error code on failure - */ -int ble_ead_decrypt(const uint8_t session_key[BLE_EAD_KEY_SIZE], - const uint8_t iv[BLE_EAD_IV_SIZE], - const uint8_t *encrypted_payload, size_t encrypted_payload_size, - uint8_t *payload, size_t payload_capacity); - -#ifdef __cplusplus -} -#endif - -#endif /* BLE_EAD_H */ diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c index f6d910a1157..7becf0ac43e 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/main/enc_adv_data_prph.c @@ -30,7 +30,7 @@ #include "esp_bt_defs.h" #include "esp_bt_main.h" #include "esp_gatt_common_api.h" -#include "ble_ead.h" +#include "esp_ble_ead.h" #define TAG "ENC_ADV_PRPH" @@ -48,7 +48,7 @@ static uint8_t unencrypted_adv_pattern[] = { }; /* Session key and IV for encryption - in real application, generate securely! */ -static ble_ead_key_material_t key_material = { +static esp_ble_ead_key_material_t key_material = { .session_key = { 0x19, 0x6a, 0x0a, 0xd1, 0x2a, 0x61, 0x20, 0x1e, 0x13, 0x6e, 0x2e, 0xd1, 0x12, 0xda, 0xa9, 0x57 @@ -71,7 +71,7 @@ static esp_ble_adv_params_t adv_params = { }; /* Calculate encrypted payload size */ -#define ENCRYPTED_ADV_DATA_LEN BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern)) +#define ENCRYPTED_ADV_DATA_LEN ESP_BLE_EAD_ENCRYPTED_PAYLOAD_SIZE(sizeof(unencrypted_adv_pattern)) /** * @brief Encrypt advertising data and set raw advertising data @@ -80,16 +80,16 @@ static void set_encrypted_adv_data(void) { esp_err_t ret; uint8_t encrypted_adv_data[ENCRYPTED_ADV_DATA_LEN]; - int rc; + esp_err_t rc; ESP_LOGI(TAG, "Data before encryption:"); ESP_LOG_BUFFER_HEX(TAG, unencrypted_adv_pattern, sizeof(unencrypted_adv_pattern)); /* Encrypt the advertising data */ - rc = ble_ead_encrypt(key_material.session_key, key_material.iv, - unencrypted_adv_pattern, sizeof(unencrypted_adv_pattern), - encrypted_adv_data); - if (rc != 0) { + rc = esp_ble_ead_encrypt(key_material.session_key, key_material.iv, + unencrypted_adv_pattern, sizeof(unencrypted_adv_pattern), + encrypted_adv_data); + if (rc != ESP_OK) { ESP_LOGE(TAG, "Encryption of adv data failed: %d", rc); return; } diff --git a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults index dafa5f42994..14e2c9afc03 100644 --- a/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults +++ b/examples/bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph/sdkconfig.defaults @@ -8,6 +8,10 @@ CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y # Enable SMP for security CONFIG_BT_BLE_SMP_ENABLE=y +# Encrypted Advertising Data APIs in Bluedroid host +CONFIG_BT_BLE_FEAT_ENC_ADV_DATA=y +CONFIG_BT_GATTS_KEY_MATERIAL_CHAR=y + # Select crypto library for EAD (Encrypted Advertising Data) # Options: CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT or CONFIG_BT_SMP_CRYPTO_STACK_MBEDTLS CONFIG_BT_SMP_CRYPTO_STACK_TINYCRYPT=y diff --git a/tools/ci/check_examples_documented.py b/tools/ci/check_examples_documented.py index 2c327ae63d6..6dc81c86436 100644 --- a/tools/ci/check_examples_documented.py +++ b/tools/ci/check_examples_documented.py @@ -32,8 +32,6 @@ KNOWN_MISSING = { 'bluetooth/bluedroid/ble/ble_compatibility_test', 'bluetooth/bluedroid/ble/ble_eddystone_receiver', 'bluetooth/bluedroid/ble/ble_eddystone_sender', - 'bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_cent', - 'bluetooth/bluedroid/ble/ble_enc_adv_data/enc_adv_data_prph', 'bluetooth/bluedroid/ble/ble_hid_device_demo', 'bluetooth/bluedroid/ble/ble_ibeacon', 'bluetooth/bluedroid/ble/ble_multi_conn/ble_multi_conn_cent',