Skip to main content
Visitor II
June 18, 2020
Solved

Out of Band Pairing

  • June 18, 2020
  • 12 replies
  • 4736 views

Hi,

I have a project where we use the STM32WB55 with the ST25R3911B as NFC module for OOB pairing.

I couldn't find any example for OOB pairing flow.

My start point is the p2p_server example and I can see there are:

  • BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.OOB_Data
  • BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.OOB_Data_Present

which I assume are relevant for my cause.

After reading AN5289 I also can tell there are:

  • aci_gap_set_oob_data()
  • aci_gap_get_oob_data() - which in my case always return an array of '0' at the

But I can't figure how to connect the dots. is there any example for OOB?

when I am invoking get oob data as below I always get OOB_Data array of '0', why?

uint8_t Address_Type = 0;

uint8_t Address[6];

// initial value is just for seeing it gets override

uint8_t OOB_Data[16] = { 0x01, 0x02, 0x03 };

uint8_t OOB_Data_Len;

uint8_t getOobDataRes = aci_gap_get_oob_data(0x01, &Address_Type, Address, &OOB_Data_Len, OOB_Data);

If there is no example I would appreciate if you can answer my questions ( a reference to the relevant functions would be awsome! :(

  • when I invoke aci_gap_set_oob_data() what should be the content of OOB_Data argument? I can see it's 16 bytes (which I assume received from outside) but what are thay stand for? is there any standard for it?
  • What is the flow I need to conduct in order to pair a device once I recieved the NDEF via NFC?
  • After the pairing process, If I want them to bond what else is required?
  • After the bonding how can I implement white list such that only the bonded device would be able to connect?

Thanks

    This topic has been closed for replies.
    Best answer by Remi QUINTIN

    a) When I invoke aci_gap_set_oob_data() what should be the content of OOB_Data argument? I can see it's 16 bytes (which I assume received from outside) but what are thay stand for? is there any standard for it?

    The encryption key to send via OOB data is generated by the controller (low layers of the RF stack running on the M0+ core)

    You can get this 16-byte key with the aci_gap_get_oob_data() function. Once you get key, it is up to you to transmit it to the other device over an NFC link.

    On the other device, you should call the aci_gap_set_oob_data() function to set up the encryption key.

    There is also another way to create the key. You can also generate your own key using the EDCH algoritm and set it as OOB data on both sides of the link. In this case, you have to use aci_gap_set_oob_data() on the Central and on the peripheral device.

    See the slide set I joint to this answer.

        

    b) What is the flow I need to conduct in order to pair a device once I recieved the NDEF via NFC?

    Answer is above and in the slide set.

    c) After the pairing process, if I want them to bond what else is required?

    Bonding has to be set as a requirement using the aci_gap_set_auth_requirement() function before any connection is created. Set the bonding parameter to 1 so that the bonding happens with the pairing.

        

    d) After the bonding how can I implement white list such that only the bonded device would be able to connect?

    I do not an example of white list management right now

    For your implementation, I recommend you to have a look at application notes AN5270 and AN5289

    12 replies

    ~Amit~Author
    Visitor II
    June 22, 2020

    Up

    Technical Moderator
    June 23, 2020

    a) When I invoke aci_gap_set_oob_data() what should be the content of OOB_Data argument? I can see it's 16 bytes (which I assume received from outside) but what are thay stand for? is there any standard for it?

    The encryption key to send via OOB data is generated by the controller (low layers of the RF stack running on the M0+ core)

    You can get this 16-byte key with the aci_gap_get_oob_data() function. Once you get key, it is up to you to transmit it to the other device over an NFC link.

    On the other device, you should call the aci_gap_set_oob_data() function to set up the encryption key.

    There is also another way to create the key. You can also generate your own key using the EDCH algoritm and set it as OOB data on both sides of the link. In this case, you have to use aci_gap_set_oob_data() on the Central and on the peripheral device.

    See the slide set I joint to this answer.

        

    b) What is the flow I need to conduct in order to pair a device once I recieved the NDEF via NFC?

    Answer is above and in the slide set.

    c) After the pairing process, if I want them to bond what else is required?

    Bonding has to be set as a requirement using the aci_gap_set_auth_requirement() function before any connection is created. Set the bonding parameter to 1 so that the bonding happens with the pairing.

        

    d) After the bonding how can I implement white list such that only the bonded device would be able to connect?

    I do not an example of white list management right now

    For your implementation, I recommend you to have a look at application notes AN5270 and AN5289

    ~Amit~Author
    Visitor II
    June 24, 2020

    Thank you Remi,

    I implemented the flow at the presentation yet I don't receive an hci_le_read_local_p256_public_key_complete_event.

    I started with the BLE_p2pServer example (FW package 1.7), and added the following lines at the end of APP_BLE_Init():

    /* USER CODE BEGIN APP_BLE_Init_2 */

    uint8_t mask[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x9F };

    if (hci_le_set_event_mask(mask)) {

    while (1);

    }

    if (hci_le_read_local_p256_public_key()) {

    while (1);

    }

    /* USER CODE END APP_BLE_Init_2 */

    later, I added my own implementation to hci_le_read_local_p256_public_key_complete_event() with the same fuction signature as the weak (just that mine is not weak) but I don't get the event.

    any ideas?

    Technical Moderator
    June 26, 2020

    in fact you should initialise the event mask in the following way.

    uint8_t ALL_EVENTS[8]={0x9F,0x01,0x00,0x00,0x00,0x00,0x00,0x00};

    hci_le_set_event_mask(ALL_EVENTS);

    status = hci_le_read_local_p256_public_key();

    Then wait for the event:

    #define HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE 0x08

    In the handler: case EVT_LE_META_EVENT:

    ~Amit~Author
    Visitor II
    July 2, 2020

    Thank you Remi for the detailed answers! it was very helpful!

    I was able to produce the flow and I am succesfully bonding 2 devices, yet I have some uncleared issues:

    1. While reading the presentation, I assumed that each pink/red block is conducted by the controller layer and dosen't require my additional interfering - am I correct?
    2. I couldn't get any hci_command_complete_event. what I did is adding a case of 'HCI_COMMAND_COMPLETE_EVT_CODE' under switch (event_pckt->evt) of SVCCTL_App_Notification(). What am I doing wrong?
    3. Right after bonding, aci_gap_get_bonded_devices() return the bonded device. but, after resetting the device I noticed that aci_gap_get_bonded_devices() returnes 0 devices. should I save myself the bonded devices? if so what additional data I should save for not requiering the pairing each time?
    4. When reaching the stage where the peripheral should be discovered, I want to allow only to known devices which I have their addresses to connect. can I set whitelist when invoking aci_gap_set_discoverable()? do you have any other implemention suggestion?

    Technical Moderator
    July 3, 2020

    1) Yes these are automatic exchanges done at RF stack level with no need for the host to interfere.

    2) 'HCI_COMMAND_COMPLETE_EVT_CODE' is returned for any asynchronous HCI command. Now I must admit I did not see this event checked in the current code. Are you able to get the HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE?

    3) Bonding must be enabled (bonding_mode=1 in the aci_gap_set_authentication_requirement function. Then when the bonding mode is enabled, the list of bonded devices is entirely managed by the controller (on M0+) and saved in Flash memory. So, you should still be able to get the bonded device list after a reset with the aci_gap_get_bonded_devices() function.

    4) device address must have been put in the white list with the aci_gap_configure_white_list command. This is to be done after the disconnection. You have to save this white list in Flash memory in order to save it in case of reset.

    Then you can use the aci_gap_set_undirected_connectable function that has a filter policy parameter for advertising (0x03: Allow scan request from white list only, allow connect request from white list only).

    ~Amit~Author
    Visitor II
    July 7, 2020

    Thanks Remi

    1. Great
    2. I am able to get the HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE but not the HCI_COMMAND_COMPLETE_EVT_CODE. this led me to a workaround of invoking aci_gap_get_oob_data and then start polling the value of OOB_Data until it has been changed. this is not a proper solution, is there a way to get an event?
    3. The bonding is enabled by BleApplicationContext.BleApplicationContext_legacy.bleSecurityParam.bonding_mode = CFG_BONDING_MODE; when CFG_BONDING_MODE is defined as 1. after the mentioned setting aci_gap_set_authentication_requirement is invoked with the relevant args. I am able to pair and bond. I invoke aci_gap_get_bonded_devices() periodically and I can see I got the remote device as bonded after bonding. the issue is that after a reset, aci_gap_get_bonded_devices() returns no bonded devices. more precisely Num_of_Addresses value is 0. I tried (not sure it should affect) setting CFG_HW_RESET_BY_FW to 0 but it didn't help. what should I do?
    4. OK. this sounds exactly what I need but the aci_gap_configure_whitelist() doesn't take any arguments so how do I insert the allowed addresses into the whitelist? also, I didn't understand the part it has to be done after the disconnection, can you please explain?

    Technical Moderator
    July 3, 2020

    I saw another point that you raise and that I did not answer

    > What is the reason I need to invoke aci_gap_set_oob_data(zeroes) on each device before getting the oob data? (I noticed that if I am not doing it the aci_gap_get_oob_data() don't work properly and return zeroes).

    In fact you have to call the aci_gap_set_oob_data(zeroes) function that enables the generation of the OOB data on the data source device when the input parameter is set to 0. Then aci_gap_get_oob_data() function enables the retrieval of the generated (thanks to aci_gap_set_oob_data(zeroes)) OOB data. This is the reason why it did not work without the aci_gap_set_oob_data(zeroes) call . This OOB data is sent via NFC to the other device that uses the aci_gap_set_oob_data(received OOB_DATA).

    Hci_le_read_local_p256_public_key() forces the generation of a new public Key each time it is called. The public keys are exchanged in between the 2 devices via the BLE connection and are used to perform some checks

    Commands used to get the generated OOB data on device data source

       Aci_gap_get_oob_data(oob_data_type=0x01-random)

       Random data received through Hci_command_complete_event

      RANDOM_DATA – local pairing data intended to the remote device to be sent via NFC

      Aci_gap_get_oob_data(oob_data_type=0x02-confirm)

      Random data received through Hci_command_complete_event

      CONFIRM_DATA – local pairing data intended to the remote device to be sent via NFC

    These 2 values are used with the Public keys by each device to perform some verifications.

    To get those oob data, you can do it this way:

     uint8_t at = 0;

    uint8_t len = 0;

     uint8_t rand[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

     uint8_t hash[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    /* Get own OOB data from device data source */

      status = aci_gap_get_oob_data(1, &at,add,&len, rand);

      status = aci_gap_get_oob_data(2, &at,add,&len, hash);

    ~Amit~Author
    Visitor II
    July 7, 2020

    It's clear now - much appriciated.

    Technical Moderator
    July 9, 2020

    1) In fact the HCI_COMMAND_COMPLETE_EVT_CODE is already managed on the CMO+ core. So you wont see it at M4 core level.

    2)The bonding list is managed internally by the CM0+ core in SRAM but is regularly saved in Flash memory when there no RF activity, especially after disconnection.

    So this list should be available after reset. one possible cause is the reset of the device before any disconnection. The CM0+ would not have time to save the bonded devices list.

    3) The white list is manage by the user using ACI function like HCI_LE_READ_WHITE_LIST_SIZE , HCI_LE_CLEAR_WHITE_LIST, HCI_LE_ADD_DEVICE_TO_WHITE_LIST, HCI_LE_REMOVE_DEVICE_FROM_WHITE_LIST.

    Have a look at the AN5270 that describes those functions.

    Graduate II
    March 1, 2022

    Hello @Remi QUINTIN​  (Employee)​ 

    I have tried implementing the OOB from a firmware example I found on the forum, and from the little nuggets of information that I got from the above posts, I put together the pieces, but I haven't been able to trigger OOB Byte in the firmware version binary 1.13.0. I am using an android Android app and device is STM32Wb series microcontroller. Irrespective of permutations and combinations that I have tried, connection always falls back to numeric pairing.. :(

    My Goal is to have Preset OOB Key on Device(STM32WB) and Phone(currently android) to establish a secure connection.

    • Added event mask to generate 256bit key after "aci_gap_set_authentication_requirement"
    uint8_t ALL_EVENTS[8]={0x9F,0x01,0x00,0x00,0x00,0x00,0x00,0x00};
     hci_le_set_event_mask(ALL_EVENTS); 
     int status = hci_le_read_local_p256_public_key();
     APP_DBG_MSG("\r\n\r EVENT MASK To Generate a LOCAL 256 Key %d \n",status );

    Added "HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE" event in

    switch (meta_evt->subevent)
     {
     .// few more cases not shown for clarity
     case HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE:
     { 
     APP_DBG_MSG("\n\r OOB Key Gen Event!");
     runOOB();
     }
    }
    1.  
    • Added a runOOB Function
    static void runOOB(void)
    {
     
     uint8_t at = 0x00; 
     uint8_t add[6] = {0x00,0x00,0x00,0x00,0x00,0x00};
     uint8_t len = 0x10;
     uint8_t random[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
     uint8_t confirm[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
     
     int status = aci_gap_set_oob_data(0x00,0x00,0x00, 0, 0, 0);
     APP_DBG_MSG("\n\r OOB INITIALIZATION STATUS : %d", status);
     
     
     int statusRand = aci_gap_get_oob_data(0x01, &at,add,&len, random);
     int statusConfirm = aci_gap_get_oob_data(0x02, &at,add,&len, confirm);
     
     
     APP_DBG_MSG("\n\r OOB Key - Random Data: [");
     
    for(int i =0; i <16; i++)
     APP_DBG_MSG("%02X:", random[i]);
     
     APP_DBG_MSG("]");
     APP_DBG_MSG("] \n\r OOB Key Hash : ");
     
    for(int i =0; i <16; i++)
     APP_DBG_MSG("%02X:", confirm[i]);
     
     
    }

    Can you please provide some guidance, on what I might be doing wrong?

    Thanks

    Technical Moderator
    April 4, 2022

    Hello,

    Did you receive HCI_LE_READ_LOCAL_P256_PUBLIC_KEY_COMPLETE_SUBEVT_CODE event ?

    Your implementation seems to be right, once you have OOB data, you need to transmit theses data to the smartphone via NFC. You must write random and confirm data on NFC shield. Can you give us more information about your setup ? Which NFC library you used ?

    Best Regards