diff --git a/bumble/helpers.py b/bumble/helpers.py index 09b86ef..e9ebf97 100644 --- a/bumble/helpers.py +++ b/bumble/helpers.py @@ -18,6 +18,8 @@ import logging from colors import color +from bumble.smp import SMP_CID, SMP_Command + from .core import name_or_number from .gatt import ATT_PDU, ATT_CID from .l2cap import ( @@ -73,6 +75,9 @@ class PacketTracer: if l2cap_pdu.cid == ATT_CID: att_pdu = ATT_PDU.from_bytes(l2cap_pdu.payload) self.analyzer.emit(att_pdu) + elif l2cap_pdu.cid == SMP_CID: + smp_command = SMP_Command.from_bytes(l2cap_pdu.payload) + self.analyzer.emit(smp_command) elif l2cap_pdu.cid == L2CAP_SIGNALING_CID or l2cap_pdu.cid == L2CAP_LE_SIGNALING_CID: control_frame = L2CAP_Control_Frame.from_bytes(l2cap_pdu.payload) self.analyzer.emit(control_frame) diff --git a/bumble/smp.py b/bumble/smp.py index 17ca6ed..c544a82 100644 --- a/bumble/smp.py +++ b/bumble/smp.py @@ -155,6 +155,7 @@ SMP_CT2_AUTHREQ = 0b00100000 SMP_CTKD_H7_LEBR_SALT = bytes.fromhex('00000000000000000000000000000000746D7031') SMP_CTKD_H7_BRLE_SALT = bytes.fromhex('00000000000000000000000000000000746D7032') + # ----------------------------------------------------------------------------- # Utils # ----------------------------------------------------------------------------- @@ -879,7 +880,7 @@ class Session: ) ) ) - + async def derive_ltk(self): link_key = await self.manager.device.get_link_key(self.connection.peer_address) assert link_key is not None @@ -914,7 +915,7 @@ class Session: csrk = bytes(16) # FIXME: testing if self.initiator_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG: self.send_command(SMP_Signing_Information_Command(signature_key=csrk)) - + # CTKD, calculate BR/EDR link key if self.initiator_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG: ilk = crypto.h7( @@ -946,7 +947,7 @@ class Session: csrk = bytes(16) # FIXME: testing if self.responder_key_distribution & SMP_SIGN_KEY_DISTRIBUTION_FLAG: self.send_command(SMP_Signing_Information_Command(signature_key=csrk)) - + # CTKD, calculate BR/EDR link key if self.responder_key_distribution & SMP_LINK_KEY_DISTRIBUTION_FLAG: ilk = crypto.h7( @@ -980,12 +981,7 @@ class Session: self.peer_expected_distributions.remove(command_class) logger.debug(f'remaining distributions: {[c.__name__ for c in self.peer_expected_distributions]}') if not self.peer_expected_distributions: - # The initiator can now send its keys - if self.is_initiator: - self.distribute_keys() - - # Nothing left to expect, we're done - asyncio.create_task(self.on_pairing()) + self.on_peer_key_distribution_complete() else: logger.warn(color(f'!!! unexpected key distribution command: {command_class.__name__}', 'red')) self.send_pairing_failed(SMP_UNSPECIFIED_REASON_ERROR) @@ -1006,12 +1002,23 @@ class Session: self.connection.remove_listener('connection_encryption_key_refresh', self.on_connection_encryption_key_refresh) self.manager.on_session_end(self) + def on_peer_key_distribution_complete(self): + # The initiator can now send its keys + if self.is_initiator: + self.distribute_keys() + + asyncio.create_task(self.on_pairing()) + def on_connection_encryption_change(self): if self.connection.is_encrypted: if self.is_responder: # The responder distributes its keys first, the initiator later self.distribute_keys() + # If we're not expecting key distributions from the peer, we're done + if not self.peer_expected_distributions: + self.on_peer_key_distribution_complete() + def on_connection_encryption_key_refresh(self): # Do as if the connection had just been encrypted self.on_connection_encryption_change() diff --git a/tests/self_test.py b/tests/self_test.py index cf1beff..7316b2f 100644 --- a/tests/self_test.py +++ b/tests/self_test.py @@ -246,8 +246,7 @@ IO_CAP = [ SC = [False, True] MITM = [False, True] # Key distribution is a 4-bit bitmask -# IdKey is necessary for current SMP structure -KEY_DIST = [i for i in range(16) if (i & SMP_ID_KEY_DISTRIBUTION_FLAG)] +KEY_DIST = range(16) @pytest.mark.asyncio @pytest.mark.parametrize('io_cap, sc, mitm, key_dist',