Search code examples
securitybluetooth-lowenergypython-cryptography

How to calculate BLE SC Pairing Confirm Value for Passkey Entry in Python?


I'm implementing some Bluetooth Low Energy functionalities in Python.

In the pairing process there is a Pairing Confirm Value like specified in Bluetooth Core Specs 5.3 page 1604 and 1562f.

I have the f4 function already, but if the Authentication Method is "Password Entry", the 6-digit code is also part of this calculation.

Given the 6-digit-Code, I want to be able to generate the Confirmation Values (I think there should be multiple of those)

I already have this (I guess working) function here:

 def generateConfirmValue(
        self, keyX=None, remoteKeyX=None, localNonce=None, rbi=b"\x00"
    ):
        """
        This class method can be used to generate a confirm value.
        :param rbi: Passkey Byte
        :type rbi: Byte
        :return: Confirm value
        :rtype: Bytes
        """
        if not (keyX and remoteKeyX and localNonce):
            if self.keyX == None or self.remoteKeyX == None:
                io.warning("Please first generate the DH keys")
                return
            if self.localNonce == None:
                io.warning("Please first generate the local Nonce")
                return
            keyX = self.keyX
            remoteKeyX = self.remoteKeyX
            localNonce = self.localNonce
        localConfirm = BLECryptoSC.f4(keyX, remoteKeyX, localNonce, rbi)
        return CryptoUtils.reverseOrder(localConfirm.hex())

from here

and now I wanna have a function like this:

def generateConfirmValueForPasskey(self, keyX, remoteKeyX, localNonce, passkey, counter):
    passkeyByteForF4 = ...
    return generateConfirmValue(keyX, remoteKeyX, localNonce, rbi=passkeyForF4)

Edit: I found a function which does the exact reverse from what I want to do: Link


Solution

  • After some more time I came up with this answer:

    The passkey has to be converted into binary, then reversed.

        passkey = int(input("passkey: "))
        binary_number = format(passkey, '020b')
        self.masterRemoteConfirmPasskeyBitString = binary_number[::-1]
        self.masterRemoteConfirmPasskeyBitCounter = 0
    

    Then I have to go through the result bit by bit and set rbi to 0x80 if the bit is 0 or to 0x81 if the bit is 1:

    rbi = None
    if(self.masterRemoteConfirmPasskeyBitString[self.masterRemoteConfirmPasskeyBitCounter] == '0'):
        rbi = b"\x80"
    else:
        rbi = b"\x81"
    self.masterRemoteConfirmPasskeyBitCounter += 1
    self.masterLocalNonce = self.masterSCCrypto.generateLocalNonce()
    nwOrderConfirmValue = self.masterSCCrypto.generateConfirmValue(rbi=rbi)
    

    As I said, this then has to be done 20 times, because we need 20 different confirm values with the according random values.