Skip to content

Commit

Permalink
feat: new bindkey_verified variable to track whether encryption is wo…
Browse files Browse the repository at this point in the history
…rking
  • Loading branch information
Jc2k committed Jul 24, 2022
1 parent 08884e3 commit dc622a5
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 77 deletions.
29 changes: 23 additions & 6 deletions src/xiaomi_ble/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,13 +922,28 @@ def decode_temps_probes(packet_value: int) -> float:
class XiaomiBluetoothDeviceData(BluetoothData):
"""Data for Xiaomi BLE sensors."""

def __init__(self, aeskey: bytes | None = None) -> None:
def __init__(self, bindkey: bytes | None = None) -> None:
super().__init__()
self.aeskey = aeskey
self.bindkey = bindkey

# Data that we know how to parse but don't yet map to the SensorData model.
self.unhandled: dict[str, Any] = {}

# The type of encryption to expect, based on flags in the bluetooth
# frame.
self.encryption_scheme = EncryptionScheme.NONE

# If true, then we know the actual MAC of the device.
# On macOS, we don't unless the device includes it in the advertisement
# (CoreBluetooth uses UUID's generated by CoreBluetooth instead of the MAC)
self.mac_known = sys.platform != "darwin"

# If true then we have used the provided encryption key to decrypt at least
# one payload.
# If false then we have either not seen an encrypted payload, the key is wrong
# or encryption is not in use
self.bindkey_verified = False

def supported(self, data: BluetoothServiceInfo) -> bool:
if not super().supported(data):
return False
Expand Down Expand Up @@ -1150,15 +1165,15 @@ def _decrypt_mibeacon_v4_v5(
if len(data) < i + 9:
_LOGGER.debug("Invalid data length (for decryption), adv: %s", data.hex())
# try to find encryption key for current device
if not self.aeskey or len(self.aeskey) != 16:
if not self.bindkey or len(self.bindkey) != 16:
_LOGGER.error("Encryption key should be 16 bytes (32 characters) long")
return None

nonce = b"".join([xiaomi_mac[::-1], data[2:5], data[-7:-4]])
aad = b"\x11"
token = data[-4:]
cipherpayload = data[i:-7]
cipher = AES.new(self.aeskey, AES.MODE_CCM, nonce=nonce, mac_len=4)
cipher = AES.new(self.bindkey, AES.MODE_CCM, nonce=nonce, mac_len=4)
cipher.update(aad)

try:
Expand All @@ -1175,6 +1190,7 @@ def _decrypt_mibeacon_v4_v5(
to_mac(xiaomi_mac),
)
return None
self.bindkey_verified = True
return decrypted_payload

def _decrypt_mibeacon_legacy(
Expand All @@ -1184,10 +1200,10 @@ def _decrypt_mibeacon_legacy(
# check for minimum length of encrypted advertisement
if len(data) < i + 7:
_LOGGER.debug("Invalid data length (for decryption), adv: %s", data.hex())
if not self.aeskey or len(self.aeskey) != 12:
if not self.bindkey or len(self.bindkey) != 12:
_LOGGER.error("Encryption key should be 12 bytes (24 characters) long")
return None
key = b"".join([self.aeskey[0:6], bytes.fromhex("8d3d3c97"), self.aeskey[6:]])
key = b"".join([self.bindkey[0:6], bytes.fromhex("8d3d3c97"), self.bindkey[6:]])

nonce = b"".join([data[0:5], data[-4:-1], xiaomi_mac[::-1][:-1]])
aad = b"\x11"
Expand All @@ -1208,4 +1224,5 @@ def _decrypt_mibeacon_legacy(
to_mac(xiaomi_mac),
)
return None
self.bindkey_verified = True
return decrypted_payload
Loading

0 comments on commit dc622a5

Please sign in to comment.