BTHome.Encryption (BTHome v0.1.0)
View SourceAES-CCM encryption/decryption for BTHome v2 protocol.
Implements the BTHome v2 encryption specification using AES-CCM mode with 16-byte keys and 4-byte MIC (Message Integrity Check).
Key Requirements
- Encryption key must be exactly 16 bytes (128-bit)
- MAC address must be exactly 6 bytes
- Counter is a 4-byte unsigned integer (little-endian)
- MIC (Message Integrity Check) is always 4 bytes
Nonce Construction
The nonce is constructed by concatenating:
- MAC address (6 bytes)
- BTHome UUID (2 bytes, always 0xD2FC)
- Device info byte (1 byte)
- Counter (4 bytes, little-endian)
Total nonce length: 13 bytes
Examples
# Encrypt sensor data
key = <<0x23, 0x1d, 0x39, 0xc1, 0xd7, 0xcc, 0x1a, 0xb1,
0xae, 0xe2, 0x24, 0xcd, 0x09, 0x6d, 0xb9, 0x32>>
mac = <<0x54, 0x48, 0xe6, 0x8f, 0x80, 0xa5>>
data = <<0x02, 0xca, 0x09, 0x03, 0xbf, 0x13>> # temp + humidity
counter = 0x33221100
{:ok, {ciphertext, mic}} = BTHome.Encryption.encrypt(data, key, mac, counter)
# Decrypt sensor data
{:ok, decrypted} = BTHome.Encryption.decrypt(ciphertext, mic, key, mac, counter)
Summary
Functions
Decrypts BTHome sensor data using AES-CCM.
Encrypts BTHome sensor data using AES-CCM.
Generates a random encryption key.
Converts a hex string to binary key.
Converts a binary key to hex string.
Functions
Decrypts BTHome sensor data using AES-CCM.
Parameters
ciphertext
- Encrypted sensor datamic
- 4-byte Message Integrity Checkkey
- 16-byte encryption key (same as used for encryption)mac_address
- 6-byte MAC address of the devicecounter
- 4-byte counter value used during encryptiondevice_info
- Device information byte (optional, defaults to encrypted v2)
Returns
{:ok, data}
- Successfully decrypted sensor data{:error, reason}
- Decryption failed (wrong key, tampered data, etc.)
Examples
{:ok, data} = decrypt(ciphertext, mic, key, mac, 1)
Encrypts BTHome sensor data using AES-CCM.
Parameters
data
- Binary sensor data to encryptkey
- 16-byte encryption keymac_address
- 6-byte MAC address of the devicecounter
- 4-byte counter value (unsigned integer)device_info
- Device information byte (optional, defaults to encrypted v2)
Returns
{:ok, {ciphertext, mic}}
- Success with encrypted data and MIC{:error, reason}
- Encryption failed
Examples
key = :crypto.strong_rand_bytes(16)
mac = <<0x54, 0x48, 0xe6, 0x8f, 0x80, 0xa5>>
data = <<0x02, 0xca, 0x09>> # temperature measurement
{:ok, {ciphertext, mic}} = encrypt(data, key, mac, 1)
Generates a random encryption key.
Returns
16-byte random encryption key
Converts a hex string to binary key.
Parameters
hex_string
- Hex string representation of key (32 characters)
Returns
{:ok, key}
- Successfully converted key{:error, reason}
- Invalid hex string
Examples
{:ok, key} = key_from_hex("231d39c1d7cc1ab1aee224cd096db932")
Converts a binary key to hex string.
Parameters
key
- Binary encryption key
Returns
Hex string representation of the key
Examples
hex = key_to_hex(key) # => "231d39c1d7cc1ab1aee224cd096db932"