Skip to content

Require encryption to write the CCCD of an encrypted characteristic (espressif) - #11236

Open
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:espressif-cccd-encryption
Open

Require encryption to write the CCCD of an encrypted characteristic (espressif)#11236
dhalbert wants to merge 1 commit into
adafruit:mainfrom
dhalbert:espressif-cccd-encryption

Conversation

@dhalbert

@dhalbert dhalbert commented Aug 23, 2026

Copy link
Copy Markdown
Collaborator

Claude found this bug and wrote the fix. I reworked this explanation for clarity.

(Part of the series of small PRs replacing #11178. Independent of the others.)

The problem

NimBLE (used on Espressif) derives the auto-generated CCCD's permissions only from the BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC/AUTHEN flags. Those flags were not set by us, so the CCCD of an encryption-requiring characteristic was writable on an unencrypted link. This meant an unpaired central could subscribe without having to pair.

By contrast, the nordic port rejects the unencrypted CCCD write with Insufficient Authentication, and that rejection then makes the OS pair.

Since no pairing happened, subsequent web workflow commands, which sent BLE Write Commands, were dropped with no error. This caused the client to just hang waiting for some file transfer results.

(Note that Write Requests can return an error because the server sends a response, but Write Commands get no response. The BLE ATT protocol requires the server to silently ignore those Write Commands when unauthorized.)

The fix

Set BLE_GATT_CHR_F_NOTIFY_INDICATE_ENC (or _AUTHEN) whenever the characteristic's read permission requires encryption (or authentication), so the CCCD write permission follows the characteristic read permission, matching the nordic port.

Note that upstream NimBLE made this inheritance automatic in apache/mynewt-nimble@bfde0dc (July 2026, first released in NimBLE 1.10.0), calling the old behavior a security bypass. But the NimBLE fork pinned by ESP-IDF 6.0.x is 1.6.0, so it doesn't include this fix.

Testing

Tested on a Metro ESP32-S3 with the web editor on Linux, verified with btmon: the CCCD Write Request is now rejected with Insufficient Authentication (0x05), which triggers SMP pairing and encryption, after which the retried write succeeds and file transfer works. Open characteristics still work without pairing, and a bonded reconnect resumes without a new pairing prompt.

@dhalbert
dhalbert requested a review from tannewt August 23, 2026 20:10
…espressif)

NimBLE derives the auto-generated CCCD's permissions only from the
BLE_GATT_CHR_F_NOTIFY_INDICATE_* flags, which were never set, so the CCCD
of an encryption-requiring characteristic was writable on an unencrypted
link. An unpaired central could subscribe, nothing ever demanded pairing,
and its file transfer commands -- Write Commands, which ATT requires the
server to silently ignore when unauthorized -- were dropped with no error,
hanging the client forever.

Now the CCCD write permission follows the characteristic read permission,
as in the nordic port. The central's subscribe attempt gets an ATT
insufficient-authentication error, which is what makes it initiate pairing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@dhalbert
dhalbert force-pushed the espressif-cccd-encryption branch from 3c3380d to 14ff34c Compare August 23, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant