Skip to content

Commit

Permalink
Fixing undeterministic bug in SMP state machine
Browse files Browse the repository at this point in the history
  • Loading branch information
RCayre committed Dec 7, 2023
1 parent f42b2bf commit 49bdb8e
Showing 1 changed file with 45 additions and 31 deletions.
76 changes: 45 additions & 31 deletions whad/ble/stack/smp/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1279,10 +1279,11 @@ def initiate_pairing(self, parameters=None):
# Save pairing request
self.state.pairing_req = pairing_req

self.send_data(pairing_req)

# Update current state
self.state.state = SecurityManagerState.STATE_PAIRING_RSP

self.send_data(pairing_req)
return True
else:
logger.info('We are in an inconsistent state.')
Expand Down Expand Up @@ -1330,13 +1331,14 @@ def on_public_key(self, public_key_pkt):

logger.debug('[send_pairing_confirm] Computed CONFIRM=%s' % hexlify(self.state.responder.confirm))

# Update current state
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT

self.send_data(
SM_Confirm(
confirm=self.state.responder.confirm[::-1]
)
)
# Update current state
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT
else:

# Set counter to 1
Expand Down Expand Up @@ -1372,13 +1374,13 @@ def on_public_key(self, public_key_pkt):
bytes([((self.state.passkey_value >> (self.state.passkey_counter - 1)) & 1) + 0x80])
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN
self.send_data(
SM_Confirm(
confirm = self.state.initiator.confirm[::-1]
)
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN


def on_security_request(self, security_request):
Expand Down Expand Up @@ -1518,6 +1520,9 @@ def on_pairing_request(self, pairing_req):
# Save pairing response
self.state.pairing_resp = pairing_resp

# Update current state
self.state.state = SecurityManagerState.STATE_PAIRING_REQ

self.send_data(pairing_resp)


Expand All @@ -1533,8 +1538,6 @@ def on_pairing_request(self, pairing_req):
# Generate the P256 keypair
self.state.private_key, self.state.public_key = self.get_custom_function("generate_p256_keypair")()

# Update current state
self.state.state = SecurityManagerState.STATE_PAIRING_REQ
else:
logger.info('Pairing declined.')

Expand Down Expand Up @@ -1630,6 +1633,10 @@ def on_dhkey_check(self, dhkey_check):
]
)
)


self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT

self.send_data(
SM_DHKey_Check(
dhkey_check=eb[::-1]
Expand All @@ -1641,9 +1648,6 @@ def on_dhkey_check(self, dhkey_check):

# Get the current link layer state
local_conn = self.get_layer('ll').state.register_encryption_key(conn_handle, self.state.ltk[::-1])


self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT
# otherwise, fail.
else:
logger.info('Invalid exchange value received, report error and return to idle.')
Expand Down Expand Up @@ -1805,23 +1809,25 @@ def on_pairing_response(self, pairing_resp):
confirm_value = SM_Confirm(
confirm = self.state.initiator.confirm[::-1]
)
self.send_data(confirm_value)

# Update current state
self.state.state = SecurityManagerState.STATE_LEGACY_PAIRING_CONFIRM_SENT

self.send_data(confirm_value)

else:

# Let's transmit our public key
own_x = bytes.fromhex("{:064x}".format(self.state.public_key.public_numbers().x))[::-1]
own_y = bytes.fromhex("{:064x}".format(self.state.public_key.public_numbers().y))[::-1]

self.state.state = SecurityManagerState.STATE_LESC_PUBKEY_SENT

self.send_data(
SM_Public_Key(
key_x = own_x,
key_y = own_y
)
)
self.state.state = SecurityManagerState.STATE_LESC_PUBKEY_SENT

else:
logger.info('Unexpected packet received, report error and return to idle.')
Expand Down Expand Up @@ -1863,11 +1869,12 @@ def on_pairing_confirm(self, confirm):
confirm_value = SM_Confirm(
confirm = self.state.responder.confirm[::-1]
)
self.send_data(confirm_value)

# Update current state
self.state.state = SecurityManagerState.STATE_LEGACY_PAIRING_CONFIRM_SENT

self.send_data(confirm_value)



elif self.state.state == SecurityManagerState.STATE_LEGACY_PAIRING_CONFIRM_SENT:

Expand All @@ -1878,9 +1885,9 @@ def on_pairing_confirm(self, confirm):
rand_value = SM_Random(
random = self.state.initiator.rand[::-1]
)
self.send_data(rand_value)

self.state.state = SecurityManagerState.STATE_LEGACY_PAIRING_RANDOM_SENT
self.send_data(rand_value)

elif self.state.state == SecurityManagerState.STATE_LESC_PUBKEY_RECVD:
logger.info('Pairing Confirm value is expected, processing ...')
Expand All @@ -1893,11 +1900,12 @@ def on_pairing_confirm(self, confirm):
rand_value = SM_Random(
random = self.state.initiator.rand[::-1]
)
self.send_data(rand_value)

# Update current state
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT

self.send_data(rand_value)

elif self.state.state in (
SecurityManagerState.STATE_LESC_PUBKEY_RECVD_STAGEN,
SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT_STAGEN
Expand All @@ -1921,27 +1929,28 @@ def on_pairing_confirm(self, confirm):
bytes([((self.state.passkey_value >> (self.state.passkey_counter - 1)) & 1) + 0x80])
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN

self.send_data(
SM_Confirm(
confirm = self.state.responder.confirm[::-1]
)
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN

elif self.state.state == SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN:
logger.info('Pairing Confirm value is expected, processing ... [iteration=%d]' % self.state.passkey_counter)

# Extract confirm value
self.state.responder.confirm = confirm.confirm[::-1]

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT_STAGEN

# Transmit random
self.send_data(
SM_Random(
random = self.state.initiator.rand[::-1]
)
)
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT_STAGEN

else:
logger.info('Pairing Confirm dropped because current state is %d' % self.state.state)
Expand Down Expand Up @@ -2017,14 +2026,14 @@ def on_pairing_random(self, random_pkt):
]
)
)
self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT
#Transmit EA
self.send_data(
SM_DHKey_Check(
dhkey_check=ea[::-1]
)
)

self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT
else:
logger.info('Invalid Numeric comparison (expected %s)' % (
str(value),
Expand Down Expand Up @@ -2104,6 +2113,10 @@ def on_pairing_random(self, random_pkt):
rand_value = SM_Random(
random = self.state.responder.rand[::-1]
)

# Next state
self.state.state = SecurityManagerState.STATE_LEGACY_PAIRING_RANDOM_SENT

self.send_data(rand_value)

# Compute our stk
Expand All @@ -2115,9 +2128,6 @@ def on_pairing_random(self, random_pkt):

logger.debug('[on_pairing_random] STK=%s' % hexlify(self.state.stk))

# Next state
self.state.state = SecurityManagerState.STATE_LEGACY_PAIRING_RANDOM_SENT

# Notify connection that we successfully negociated STK and that
# the corresponding material is available.

Expand Down Expand Up @@ -2186,13 +2196,13 @@ def on_pairing_random(self, random_pkt):
)
)

self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT
# Then transmit it
self.send_data(
SM_DHKey_Check(
dhkey_check = ea[::-1]
)
)
self.state.state = SecurityManagerState.STATE_LESC_DHK_CHECK_SENT

else:
# Increment counter
Expand All @@ -2208,13 +2218,14 @@ def on_pairing_random(self, random_pkt):
bytes([((self.state.passkey_value >> (self.state.passkey_counter - 1)) & 1) + 0x80])
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN

self.send_data(
SM_Confirm(
confirm = self.state.initiator.confirm[::-1]
)
)

self.state.state = SecurityManagerState.STATE_LESC_PAIRING_CONFIRM_SENT_STAGEN
else:

logger.info('Invalid responder CONFIRM value (expected %s)' % (
Expand Down Expand Up @@ -2246,17 +2257,18 @@ def on_pairing_random(self, random_pkt):
)

if computed_confirm == self.state.initiator.confirm:
self.send_data(
SM_Random(
random = self.state.responder.rand[::-1]
)
)

if self.state.passkey_counter == 20:
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT
else:
self.state.passkey_counter += 1
self.state.state = SecurityManagerState.STATE_LESC_PAIRING_RANDOM_SENT_STAGEN

self.send_data(
SM_Random(
random = self.state.responder.rand[::-1]
)
)
else:

logger.info('Invalid Initiator CONFIRM value (expected %s)' % (
Expand Down Expand Up @@ -2450,12 +2462,14 @@ def perform_key_distribution(self):
if self.state.responder.must_dist_csrk():
logger.info('[smp] sending generated CSRK ...')
self.state.csrk = self.get_custom_function("generate_csrk")()

self.state.state = SecurityManagerState.STATE_DISTRIBUTE_KEY

self.send_data(SM_Signing_Information(
csrk=self.state.csrk
))
logger.info('[smp] CSRK sent.')

self.state.state = SecurityManagerState.STATE_DISTRIBUTE_KEY

def on_encryption_information(self, encryption_information):
if self.is_initiator():
Expand Down

0 comments on commit 49bdb8e

Please sign in to comment.