src.bluetooth_sig.advertising.ead_decryptor¶
Pure decryption functions for BLE Encrypted Advertising Data (EAD).
This module provides framework-agnostic decryption functions for EAD per Bluetooth Core Spec Supplement Section 1.23.
The decryption uses AES-CCM with: - 128-bit (16-byte) session key - 13-byte nonce (5-byte randomizer + 6-byte device address + 2-byte padding) - 4-byte MIC (authentication tag)
Requires the ‘cryptography’ package: pip install bluetooth-sig[ead]
Attributes¶
Name | Description |
|---|---|
Classes¶
Name | Description |
|---|---|
Stateful EAD decryptor with cipher caching and key provider support. |
Functions¶
Name | Description |
|---|---|
|
Build the 13-byte nonce for EAD decryption. |
|
Decrypt BLE Encrypted Advertising Data. |
|
Convenience function to decrypt raw EAD bytes. |
Module Contents¶
- class src.bluetooth_sig.advertising.ead_decryptor.EADDecryptor(*, _key_provider: bluetooth_sig.advertising.encryption.EADKeyProvider | None = None, _static_key: bytes | None = None)¶
Stateful EAD decryptor with cipher caching and key provider support.
For one-off decryption, use the module-level decrypt_ead() function. For repeated decryption with the same key, this class caches the AESCCM cipher instance for better performance.
This class also integrates with EADKeyProvider for automatic key lookup by MAC address.
Use the factory methods from_key() or from_provider() to create instances.
- _key_provider¶
Optional key provider for MAC-based key lookup
- _static_key¶
Static session key (used if no provider)
- _cipher_cache¶
Cached cipher instances keyed by session key
- Example with static key:
>>> from bluetooth_sig.advertising import EADDecryptor >>> >>> decryptor = EADDecryptor.from_key(bytes.fromhex("0123456789abcdef0123456789abcdef")) >>> result = decryptor.decrypt(raw_ead_data, "AA:BB:CC:DD:EE:FF") >>> if result.success: ... print(result.plaintext)
- Example with key provider:
>>> from bluetooth_sig.advertising import EADDecryptor, DictKeyProvider >>> from bluetooth_sig.types.ead import EADKeyMaterial >>> >>> provider = DictKeyProvider() >>> provider.set_ead_key( ... "AA:BB:CC:DD:EE:FF", ... EADKeyMaterial( ... session_key=bytes.fromhex("0123456789abcdef0123456789abcdef"), ... iv=bytes.fromhex("0102030405060708"), ... ), ... ) >>> decryptor = EADDecryptor.from_provider(provider) >>> result = decryptor.decrypt(raw_ead_data, "AA:BB:CC:DD:EE:FF")
- clear_cache() None¶
Clear the cipher cache.
Call this if you need to free memory or if keys have been rotated.
- decrypt(raw_ead_data: bytes, mac_address: str, associated_data: bytes | None = None) bluetooth_sig.types.ead.EADDecryptResult¶
Decrypt EAD data using cached cipher.
Looks up the session key from the key provider (if configured) or uses the static key. Caches the cipher instance for reuse.
- Parameters:
raw_ead_data – Raw EAD advertisement bytes (AD Type 0x31 payload)
mac_address – Device MAC address (e.g., “AA:BB:CC:DD:EE:FF”)
associated_data – Optional additional authenticated data (AAD)
- Returns:
EADDecryptResult with success status and plaintext or error details
- classmethod from_key(session_key: bytes) EADDecryptor¶
Create decryptor with a static session key.
- Parameters:
session_key – 16-byte AES-128 session key
- Returns:
Configured EADDecryptor instance
- Raises:
ValueError – If session_key is not 16 bytes
Example
>>> decryptor = EADDecryptor.from_key(bytes.fromhex("0123456789abcdef0123456789abcdef"))
- classmethod from_provider(key_provider: bluetooth_sig.advertising.encryption.EADKeyProvider) EADDecryptor¶
Create decryptor with a key provider for MAC-based lookup.
- Parameters:
key_provider – Provider that looks up keys by MAC address
- Returns:
Configured EADDecryptor instance
Example
>>> provider = DictKeyProvider() >>> provider.set_ead_key("AA:BB:CC:DD:EE:FF", key_material) >>> decryptor = EADDecryptor.from_provider(provider)
- src.bluetooth_sig.advertising.ead_decryptor.build_ead_nonce(randomizer: bytes, device_address: bytes) bytes¶
Build the 13-byte nonce for EAD decryption.
- The nonce is constructed as:
Randomizer[5] + DeviceAddress[6] + Padding[2]
- Parameters:
randomizer – 5-byte randomizer from EAD advertisement
device_address – 6-byte BLE device address (little-endian)
- Returns:
13-byte nonce for AES-CCM decryption
- Raises:
ValueError – If randomizer is not 5 bytes or address is not 6 bytes
Example
>>> randomizer = bytes.fromhex("0102030405") >>> address = bytes.fromhex("aabbccddeeff") >>> nonce = build_ead_nonce(randomizer, address) >>> len(nonce) 13
- src.bluetooth_sig.advertising.ead_decryptor.decrypt_ead(encrypted_data: bluetooth_sig.types.ead.EncryptedAdvertisingData, session_key: bytes, device_address: bytes, associated_data: bytes | None = None) bluetooth_sig.types.ead.EADDecryptResult¶
Decrypt BLE Encrypted Advertising Data.
Performs AES-CCM decryption per Bluetooth Core Spec Supplement Section 1.23. This function never raises exceptions on decryption failure; instead, it returns a result with appropriate error information.
- Parameters:
encrypted_data – Parsed EAD structure containing randomizer, encrypted payload, and MIC
session_key – 16-byte AES-128 session key
device_address – 6-byte BLE device address (little-endian bytes)
associated_data – Optional additional authenticated data (AAD). Must match the AAD used during encryption.
- Returns:
EADDecryptResult with success status and plaintext or error details
Example
>>> ead = EncryptedAdvertisingData.from_bytes(raw_advertisement) >>> result = decrypt_ead(ead, session_key, device_address) >>> if result.success: ... process_sensor_data(result.plaintext) ... elif result.error_type == EADError.INVALID_KEY: ... logger.warning("Incorrect encryption key for device")
- src.bluetooth_sig.advertising.ead_decryptor.decrypt_ead_from_raw(raw_ead_data: bytes, session_key: bytes, mac_address: str, associated_data: bytes | None = None) bluetooth_sig.types.ead.EADDecryptResult¶
Convenience function to decrypt raw EAD bytes.
Combines parsing and decryption in a single call for simpler usage.
- Parameters:
raw_ead_data – Raw EAD advertisement bytes (AD Type 0x31 payload)
session_key – 16-byte AES-128 session key
mac_address – Device MAC address string (e.g., “AA:BB:CC:DD:EE:FF”)
associated_data – Optional additional authenticated data
- Returns:
EADDecryptResult with success status and plaintext or error details
Example
>>> result = decrypt_ead_from_raw( ... raw_ead_data=advertisement_payload, ... session_key=bytes.fromhex("0123456789abcdef0123456789abcdef"), ... mac_address="AA:BB:CC:DD:EE:FF", ... ) >>> if result.success: ... print(f"Decrypted: {result.plaintext.hex()}")
- src.bluetooth_sig.advertising.ead_decryptor.logger¶