BTHome.Encryption (BTHome v0.1.0)

View Source

AES-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:

  1. MAC address (6 bytes)
  2. BTHome UUID (2 bytes, always 0xD2FC)
  3. Device info byte (1 byte)
  4. 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

decrypt(ciphertext, mic, key, mac_address, counter)

Decrypts BTHome sensor data using AES-CCM.

Parameters

  • ciphertext - Encrypted sensor data
  • mic - 4-byte Message Integrity Check
  • key - 16-byte encryption key (same as used for encryption)
  • mac_address - 6-byte MAC address of the device
  • counter - 4-byte counter value used during encryption
  • device_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)

encrypt(data, key, mac_address, counter)

Encrypts BTHome sensor data using AES-CCM.

Parameters

  • data - Binary sensor data to encrypt
  • key - 16-byte encryption key
  • mac_address - 6-byte MAC address of the device
  • counter - 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)

generate_key()

Generates a random encryption key.

Returns

16-byte random encryption key

key_from_hex(hex_string)

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")

key_to_hex(key)

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"