This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

How is the LESC OOB data generated?

The OOB data for LESC pairing contains the BLE GAP Address 256bit random and 256bit confirm value, and is generated by sd_ble_gap_lesc_oob_data_get using the connection handle and public key. sd_ble_gap_lesc_oob_data_set sets the OOB data when the BLE_GAP_EVT_LESC_DHKEY_REQUEST event occurs. However when I tried to test this by providing the central and peripheral with OOB data that was generated but changed two bytes(same on both sides though) the pairing fails.

So the function sd_ble_gap_lesc_oob_data_get generates the OOB data in a specific way?

Parents
  • The content of the OOB data is spec defined,
    so let's assume that you simply want to know how OOB data is generated according to the spec.

    By changing 2 Bytes - even on both sides - it is quite natural that you fail the pairing.
    Have a look at the spec and notice that the confirmation value is a computed value.
    It is something that peers compute on their own and then simply compare to what they received in the OOB data.

    Have a look a the following python snippet I used once upon a time to understand on my own the OOB data:

    from Crypto.Random import get_random_bytes
    from Crypto.Hash import CMAC
    from Crypto.Cipher import AES
    from Crypto.PublicKey import ECC
    
    """
    From v5.1 Vol 3 - Part H - Section 2.2.6
    The confirm value generation function makes use of the MAC function AES-CMAC_X, 
    with a 128-bit key X.
    
    The inputs to function f4 are:
    - U is 256 bits
    - V is 256 bits
    - Z is 8 bits.
      For Numeric Comparison and OOB protocol - all zeros.
    - X is 128 bits
    
    U, V and Z are concatenated and used as input m to the function AES-CMAC.
    X is used as the key k.
    
    f4(U, V, X, Z) = AES-CMAC_X(U||V||Z)
    
    The inputs to f4 are different depending on different association models.
    For OOB f4(PKx, PKx, r, 0) where r is a random value.
    """
    
    key = ECC.generate(curve='P-256')
    PKx = key.public_key()._point.x.to_bytes()
    Z = b'\x00\x00\x00\x00\x00\x00\x00\x00'
    r = get_random_bytes(16)
    
    cobj = CMAC.new(r, ciphermod=AES)
    cobj.update(PKx + PKx + Z)
    
    
    print(f'For your reference, this is the private key: {key.d.to_bytes().hex()}')
    print(f'OOB: PKx: {PKx.hex()}')
    print(f'OOB: PKy: {key.public_key()._point.y.to_bytes().hex()}')
    print(f'OOB: r: {r.hex()}')
    print(f'OOB: Confirmation: {cobj.hexdigest()}')
    

Reply
  • The content of the OOB data is spec defined,
    so let's assume that you simply want to know how OOB data is generated according to the spec.

    By changing 2 Bytes - even on both sides - it is quite natural that you fail the pairing.
    Have a look at the spec and notice that the confirmation value is a computed value.
    It is something that peers compute on their own and then simply compare to what they received in the OOB data.

    Have a look a the following python snippet I used once upon a time to understand on my own the OOB data:

    from Crypto.Random import get_random_bytes
    from Crypto.Hash import CMAC
    from Crypto.Cipher import AES
    from Crypto.PublicKey import ECC
    
    """
    From v5.1 Vol 3 - Part H - Section 2.2.6
    The confirm value generation function makes use of the MAC function AES-CMAC_X, 
    with a 128-bit key X.
    
    The inputs to function f4 are:
    - U is 256 bits
    - V is 256 bits
    - Z is 8 bits.
      For Numeric Comparison and OOB protocol - all zeros.
    - X is 128 bits
    
    U, V and Z are concatenated and used as input m to the function AES-CMAC.
    X is used as the key k.
    
    f4(U, V, X, Z) = AES-CMAC_X(U||V||Z)
    
    The inputs to f4 are different depending on different association models.
    For OOB f4(PKx, PKx, r, 0) where r is a random value.
    """
    
    key = ECC.generate(curve='P-256')
    PKx = key.public_key()._point.x.to_bytes()
    Z = b'\x00\x00\x00\x00\x00\x00\x00\x00'
    r = get_random_bytes(16)
    
    cobj = CMAC.new(r, ciphermod=AES)
    cobj.update(PKx + PKx + Z)
    
    
    print(f'For your reference, this is the private key: {key.d.to_bytes().hex()}')
    print(f'OOB: PKx: {PKx.hex()}')
    print(f'OOB: PKy: {key.public_key()._point.y.to_bytes().hex()}')
    print(f'OOB: r: {r.hex()}')
    print(f'OOB: Confirmation: {cobj.hexdigest()}')
    

Children
No Data
Related