You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@mynewt.apache.org by prasad <pr...@espressif.com> on 2019/02/11 13:40:30 UTC

Storing Bonding information in NVS memory

Hi all,

Are there any pointers/documentation for storing NIMBLE bond information 
in NVS memory?

I tried to quickly implement simple application by modifying 
'*ble_store_ram.c*'. I modified 
/ble_hs_cfg.store_read_cb/store_write_cb/store_delete_cb/ callbacks 
according to the requirement. I find this much of change is not 
sufficient to get NVS bond stored in NVS as I am continuously getting 
disconnect HCI event.

FYI, I am putting snippet of my code for each of these 3 callbacks which 
will help you understand the changes I have made.

1.**/*ble_hs_cfg.store_read_cb* :
/

/case BLE_STORE_OBJ_TYPE_OUR_SEC:
         nvs_open("nimble_bond", NVS_READWRITE, &nimble_handle);

         BLE_HS_LOG(DEBUG, "looking up our sec; ");
         err = nvs_get_blob(nimble_handle, "our_sec_key", NULL, 
&required_size);
         if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
         if (required_size == 0) {
             nvs_close(nimble_handle);
             return BLE_HS_ENOENT;
         } else {
             struct ble_store_key_sec* our_sec_key = malloc(required_size);
             err = nvs_get_blob(nimble_handle, "our_sec_key", 
our_sec_key, &required_size);
             if (err != ESP_OK) {
                 free(our_sec_key);
                 return err;
             }

             if (ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)) {
                 if (ble_addr_cmp(&our_sec_key->peer_addr, 
&key->sec.peer_addr)){
             err = nvs_get_blob(nimble_handle, "our_sec", NULL, 
&required_size);
             if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;

             struct ble_store_value_sec* our_sec = malloc(required_size);
             err = nvs_get_blob(nimble_handle, "our_sec", our_sec, 
&required_size);
             if (err != ESP_OK) {
                 free(our_sec);
                 return err;
             }

             memcpy(&value->sec, our_sec, required_size);
             free(our_sec);
                 }
             }

             free(our_sec_key);
         }
         /* NVS Close */
         nvs_close(nimble_handle);

/

/2. ///*ble_hs_cfg.store_write_cb
*//

//case BLE_STORE_OBJ_TYPE_OUR_SEC:
         ble_store_key_from_value_sec(&key_sec, &val->sec);

         required_size = sizeof(struct ble_store_key_sec);
         err = nvs_set_blob(nimble_handle, "our_sec_key", &key_sec, 
required_size);
         required_size = sizeof(struct ble_store_value_sec);
         err = nvs_set_blob(nimble_handle, "our_sec", &val->sec, 
required_size);

         /* NVS Commit */
         err = nvs_commit(nimble_handle);
         if (err != ESP_OK) return err;
         /* NVS Close */
         nvs_close(nimble_handle);

         return 0;//

Regards

Prasad


Re: Storing Bonding information in NVS memory

Posted by Christopher Collins <ch...@runtime.io>.
Hi Prasad,

On Mon, Feb 11, 2019 at 07:10:30PM +0530, prasad wrote:
> Hi all,
> 
> Are there any pointers/documentation for storing NIMBLE bond information 
> in NVS memory?

By NVS, do you mean non-volatile storage (e.g., flash)?  I believe the
`@apache-mynewt-nimble/nimble/host/store/config` package is what you
want.  This package stores bonding records to flash using the
`sys/config` package.  Typically, `sys/config` is implemented using a
flash circular buffer (FCB).

The `bleprph` sample app uses the config store, so you might look there
for a source of inspiration.

Chris

Re: Storing Bonding information in NVS memory

Posted by Andrzej Kaczmarek <an...@codecoup.pl>.
Hi Prasad,

On Fri, Feb 15, 2019 at 1:24 PM prasad <pr...@espressif.com> wrote:

> Hi Chris,
>
> Currently I have been able to develop an app which is able to store bond
> information in NVS flash. If my device (ESP32) based on NIMBLE stack is
> rebooted, the bond remains intact. But if the other device is rebooted
> the ` key_sec->peer_addr.val` gets changed based on
> `key_sec->peer_addr.type = 1` (static address, random device address).
>
> As I understand, it is expected of static address type to change to new
> value, generated after each power cycle.
>

It is allowed, but not required - device may use single static address for
its lifetime.


> So in this case how to keep bond between devices intact ?
>
> If in case static address type is not to be used for bonded devices then
> why our NIMBLE stack uses static type of address even after setting
> `bonding` flag to 1 ?
>

Static address can be used to create bond, there's nothing wrong here.
However, if you or any other vendor devices decide that their device will
use new static random address after each power cycle, then it's basically
considered as a "new" device and all bonds created using previous static
address are effectively lost. There's nothing you can do here as this is
"by design". For reference, see Core 5.0 spec, Vol 6, Part B, section 1.3.6.


> Could you help me resolve this issue? Or I am missing on something obvious?
>
> Below is the snippet of code which tries to retrieve index from database
> but fails to do so since the `peer_addr.val` field has been changed.
>
> `
>
> if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) {
>              if (*ble_addr_cmp(&cur->peer_addr, &key_sec->peer_addr*)) {
>                  continue;
> `
>
> Regards
>
> Prasad
>

Best,
Andrzej

Re: Storing Bonding information in NVS memory

Posted by prasad <pr...@espressif.com>.
Hi Chris,

Currently I have been able to develop an app which is able to store bond 
information in NVS flash. If my device (ESP32) based on NIMBLE stack is 
rebooted, the bond remains intact. But if the other device is rebooted 
the ` key_sec->peer_addr.val` gets changed based on 
`key_sec->peer_addr.type = 1` (static address, random device address).

As I understand, it is expected of static address type to change to new 
value, generated after each power cycle.

So in this case how to keep bond between devices intact ?

If in case static address type is not to be used for bonded devices then 
why our NIMBLE stack uses static type of address even after setting 
`bonding` flag to 1 ?

Could you help me resolve this issue? Or I am missing on something obvious?

Below is the snippet of code which tries to retrieve index from database 
but fails to do so since the `peer_addr.val` field has been changed.

`

if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) {
             if (*ble_addr_cmp(&cur->peer_addr, &key_sec->peer_addr*)) {
                 continue;
`

Regards

Prasad

On 14/02/19 11:53 AM, prasad wrote:
>
> Hi Chris,
>
> Thank you for your response. As I build my understanding from your 
> response,
>
> The `idx` field most probably is useful in searching the case where 
> `key.sec.peer_addr = *BLE_ADDR_ANY`, which most probably is useful to 
> search our security keys. Correct me if I am wrong.
>
> The reason behind asking use-case of `idx` was, when we are to write 
> security keys to database, we call `ble_store_key_from_value_sec` 
> which does the following:
>
> {
>         out_key->peer_addr = value->peer_addr;
>
>         out_key->ediv = value->ediv;
>         out_key->rand_num = value->rand_num;
>         out_key->ediv_rand_present = 1;
> *o**ut_key->idx = 0;*  // /This made me wonder why we need to search 
> based on this criteria./
> }
>
> Thank you for response anyways. Really helpful.
>
> Regards
>
> Prasad
>
> On 13/02/19 9:44 PM, Christopher Collins wrote:
>> Hi Prasad,
>>
>> On Wed, Feb 13, 2019 at 03:13:26PM +0530, prasad wrote:
>>> Hi all,
>>>
>>> As it happens, I fixed the bug in my code. It now correctly retrieves
>>> LTKs and bond is maintained even after reboot.
>>>
>>> Apart from this I just wanted to understand reason behind including
>>> 'idx' in structure 'ble_store_key_sec'. Could you please help me
>>> understand use-case behind including 'idx'?
>>>
>>> /** Number of results to skip; 0 means retrieve the first match. */
>>>           uint8_t idx;
>> The `idx` field is useful when your search criteria matches several
>> entries and you want to process them one by one.  For example, the
>> `ble_store_iterate()` function constructs a `ble_store_key_sec` object
>> with the following values:
>>
>>      {
>>          /**
>>           * Key by peer identity address;
>>           * peer_addr=BLE_ADDR_NONE means don't key off peer.
>>           */
>>          peer_addr = *BLE_ADDR_ANY,
>>
>>          /** Key by ediv; ediv_rand_present=0 means don't key off ediv. */
>>          ediv = 0,
>>
>>          /** Key by rand_num; ediv_rand_present=0 means don't key off rand_num. */
>>          rand_num = 0
>>
>>          ediv_rand_present = 0,
>>
>>          /** Number of results to skip; 0 means retrieve the first match. */
>>          idx = 0,
>>      }
>>
>> Then it repeatedly calls `ble_store_read()`, incrementing the `idx`
>> member each time.  This allows all stored bonds to be processed.
>>
>> Chris)

Re: Storing Bonding information in NVS memory

Posted by prasad <pr...@espressif.com>.
Hi Chris,

Thank you for your response. As I build my understanding from your response,

The `idx` field most probably is useful in searching the case where 
`key.sec.peer_addr = *BLE_ADDR_ANY`, which most probably is useful to 
search our security keys. Correct me if I am wrong.

The reason behind asking use-case of `idx` was, when we are to write 
security keys to database, we call `ble_store_key_from_value_sec` which 
does the following:

{
         out_key->peer_addr = value->peer_addr;

         out_key->ediv = value->ediv;
         out_key->rand_num = value->rand_num;
         out_key->ediv_rand_present = 1;
*o**ut_key->idx = 0;*  // /This made me wonder why we need to search 
based on this criteria./
}

Thank you for response anyways. Really helpful.

Regards

Prasad

On 13/02/19 9:44 PM, Christopher Collins wrote:
> Hi Prasad,
>
> On Wed, Feb 13, 2019 at 03:13:26PM +0530, prasad wrote:
>> Hi all,
>>
>> As it happens, I fixed the bug in my code. It now correctly retrieves
>> LTKs and bond is maintained even after reboot.
>>
>> Apart from this I just wanted to understand reason behind including
>> 'idx' in structure 'ble_store_key_sec'. Could you please help me
>> understand use-case behind including 'idx'?
>>
>> /** Number of results to skip; 0 means retrieve the first match. */
>>           uint8_t idx;
> The `idx` field is useful when your search criteria matches several
> entries and you want to process them one by one.  For example, the
> `ble_store_iterate()` function constructs a `ble_store_key_sec` object
> with the following values:
>
>      {
>          /**
>           * Key by peer identity address;
>           * peer_addr=BLE_ADDR_NONE means don't key off peer.
>           */
>          peer_addr = *BLE_ADDR_ANY,
>
>          /** Key by ediv; ediv_rand_present=0 means don't key off ediv. */
>          ediv = 0,
>
>          /** Key by rand_num; ediv_rand_present=0 means don't key off rand_num. */
>          rand_num = 0
>
>          ediv_rand_present = 0,
>
>          /** Number of results to skip; 0 means retrieve the first match. */
>          idx = 0,
>      }
>
> Then it repeatedly calls `ble_store_read()`, incrementing the `idx`
> member each time.  This allows all stored bonds to be processed.
>
> Chris)

Re: Storing Bonding information in NVS memory

Posted by Christopher Collins <ch...@runtime.io>.
Hi Prasad,

On Wed, Feb 13, 2019 at 03:13:26PM +0530, prasad wrote:
> Hi all,
> 
> As it happens, I fixed the bug in my code. It now correctly retrieves 
> LTKs and bond is maintained even after reboot.
> 
> Apart from this I just wanted to understand reason behind including 
> 'idx' in structure 'ble_store_key_sec'. Could you please help me 
> understand use-case behind including 'idx'?
> 
> /** Number of results to skip; 0 means retrieve the first match. */
>          uint8_t idx;

The `idx` field is useful when your search criteria matches several
entries and you want to process them one by one.  For example, the
`ble_store_iterate()` function constructs a `ble_store_key_sec` object
with the following values:

    {
        /**
         * Key by peer identity address;
         * peer_addr=BLE_ADDR_NONE means don't key off peer.
         */
        peer_addr = *BLE_ADDR_ANY,

        /** Key by ediv; ediv_rand_present=0 means don't key off ediv. */
        ediv = 0,

        /** Key by rand_num; ediv_rand_present=0 means don't key off rand_num. */
        rand_num = 0

        ediv_rand_present = 0,

        /** Number of results to skip; 0 means retrieve the first match. */
        idx = 0,
    }

Then it repeatedly calls `ble_store_read()`, incrementing the `idx`
member each time.  This allows all stored bonds to be processed.

Chris

Re: Storing Bonding information in NVS memory

Posted by prasad <pr...@espressif.com>.
Hi all,

As it happens, I fixed the bug in my code. It now correctly retrieves 
LTKs and bond is maintained even after reboot.

Apart from this I just wanted to understand reason behind including 
'idx' in structure 'ble_store_key_sec'. Could you please help me 
understand use-case behind including 'idx'?

/** Number of results to skip; 0 means retrieve the first match. */
         uint8_t idx;

Regards

Prasad

On 12/02/19 7:35 PM, prasad wrote:
>
> Hi all,
>
> With further debug effort the disconnect happens with HCI error 0x3d: 
> BLE_ERR_CONN_TERM_MIC.
>
> Once the connection is establish, if we disconnect and try to 
> reconnect it is disconnecting with said reason.
>
> I am attaching logs with additional prints. Please help me to know if 
> I am making any obvious mistake.
>
>
> Regards
>
> Prasad
>
> On 11/02/19 7:10 PM, prasad wrote:
>>
>> Hi all,
>>
>> Are there any pointers/documentation for storing NIMBLE bond 
>> information in NVS memory?
>>
>> I tried to quickly implement simple application by modifying 
>> '*ble_store_ram.c*'. I modified 
>> /ble_hs_cfg.store_read_cb/store_write_cb/store_delete_cb/ callbacks 
>> according to the requirement. I find this much of change is not 
>> sufficient to get NVS bond stored in NVS as I am continuously getting 
>> disconnect HCI event.
>>
>> FYI, I am putting snippet of my code for each of these 3 callbacks 
>> which will help you understand the changes I have made.
>>
>> 1.**/*ble_hs_cfg.store_read_cb* :
>> /
>>
>> /case BLE_STORE_OBJ_TYPE_OUR_SEC:
>>         nvs_open("nimble_bond", NVS_READWRITE, &nimble_handle);
>>
>>         BLE_HS_LOG(DEBUG, "looking up our sec; ");
>>         err = nvs_get_blob(nimble_handle, "our_sec_key", NULL, 
>> &required_size);
>>         if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
>>         if (required_size == 0) {
>>             nvs_close(nimble_handle);
>>             return BLE_HS_ENOENT;
>>         } else {
>>             struct ble_store_key_sec* our_sec_key = 
>> malloc(required_size);
>>             err = nvs_get_blob(nimble_handle, "our_sec_key", 
>> our_sec_key, &required_size);
>>             if (err != ESP_OK) {
>>                 free(our_sec_key);
>>                 return err;
>>             }
>>
>>             if (ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)) {
>>                 if (ble_addr_cmp(&our_sec_key->peer_addr, 
>> &key->sec.peer_addr)){
>>             err = nvs_get_blob(nimble_handle, "our_sec", NULL, 
>> &required_size);
>>             if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return 
>> err;
>>
>>             struct ble_store_value_sec* our_sec = malloc(required_size);
>>             err = nvs_get_blob(nimble_handle, "our_sec", our_sec, 
>> &required_size);
>>             if (err != ESP_OK) {
>>                 free(our_sec);
>>                 return err;
>>             }
>>
>>             memcpy(&value->sec, our_sec, required_size);
>>             free(our_sec);
>>                 }
>>             }
>>
>>             free(our_sec_key);
>>         }
>>         /* NVS Close */
>>         nvs_close(nimble_handle);
>>
>> /
>>
>> /2. ///*ble_hs_cfg.store_write_cb
>> *//
>>
>> //case BLE_STORE_OBJ_TYPE_OUR_SEC:
>>         ble_store_key_from_value_sec(&key_sec, &val->sec);
>>
>>         required_size = sizeof(struct ble_store_key_sec);
>>         err = nvs_set_blob(nimble_handle, "our_sec_key", &key_sec, 
>> required_size);
>>         required_size = sizeof(struct ble_store_value_sec);
>>         err = nvs_set_blob(nimble_handle, "our_sec", &val->sec, 
>> required_size);
>>
>>         /* NVS Commit */
>>         err = nvs_commit(nimble_handle);
>>         if (err != ESP_OK) return err;
>>         /* NVS Close */
>>         nvs_close(nimble_handle);
>>
>>         return 0;//
>>
>> Regards
>>
>> Prasad
>>

Re: Storing Bonding information in NVS memory

Posted by prasad <pr...@espressif.com>.
Hi all,

With further debug effort the disconnect happens with HCI error 0x3d: 
BLE_ERR_CONN_TERM_MIC.

Once the connection is establish, if we disconnect and try to reconnect 
it is disconnecting with said reason.

I am attaching logs with additional prints. Please help me to know if I 
am making any obvious mistake.


Regards

Prasad

On 11/02/19 7:10 PM, prasad wrote:
>
> Hi all,
>
> Are there any pointers/documentation for storing NIMBLE bond 
> information in NVS memory?
>
> I tried to quickly implement simple application by modifying 
> '*ble_store_ram.c*'. I modified 
> /ble_hs_cfg.store_read_cb/store_write_cb/store_delete_cb/ callbacks 
> according to the requirement. I find this much of change is not 
> sufficient to get NVS bond stored in NVS as I am continuously getting 
> disconnect HCI event.
>
> FYI, I am putting snippet of my code for each of these 3 callbacks 
> which will help you understand the changes I have made.
>
> 1.**/*ble_hs_cfg.store_read_cb* :
> /
>
> /case BLE_STORE_OBJ_TYPE_OUR_SEC:
>         nvs_open("nimble_bond", NVS_READWRITE, &nimble_handle);
>
>         BLE_HS_LOG(DEBUG, "looking up our sec; ");
>         err = nvs_get_blob(nimble_handle, "our_sec_key", NULL, 
> &required_size);
>         if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
>         if (required_size == 0) {
>             nvs_close(nimble_handle);
>             return BLE_HS_ENOENT;
>         } else {
>             struct ble_store_key_sec* our_sec_key = malloc(required_size);
>             err = nvs_get_blob(nimble_handle, "our_sec_key", 
> our_sec_key, &required_size);
>             if (err != ESP_OK) {
>                 free(our_sec_key);
>                 return err;
>             }
>
>             if (ble_addr_cmp(&key->sec.peer_addr, BLE_ADDR_ANY)) {
>                 if (ble_addr_cmp(&our_sec_key->peer_addr, 
> &key->sec.peer_addr)){
>             err = nvs_get_blob(nimble_handle, "our_sec", NULL, 
> &required_size);
>             if (err != ESP_OK && err != ESP_ERR_NVS_NOT_FOUND) return err;
>
>             struct ble_store_value_sec* our_sec = malloc(required_size);
>             err = nvs_get_blob(nimble_handle, "our_sec", our_sec, 
> &required_size);
>             if (err != ESP_OK) {
>                 free(our_sec);
>                 return err;
>             }
>
>             memcpy(&value->sec, our_sec, required_size);
>             free(our_sec);
>                 }
>             }
>
>             free(our_sec_key);
>         }
>         /* NVS Close */
>         nvs_close(nimble_handle);
>
> /
>
> /2. ///*ble_hs_cfg.store_write_cb
> *//
>
> //case BLE_STORE_OBJ_TYPE_OUR_SEC:
>         ble_store_key_from_value_sec(&key_sec, &val->sec);
>
>         required_size = sizeof(struct ble_store_key_sec);
>         err = nvs_set_blob(nimble_handle, "our_sec_key", &key_sec, 
> required_size);
>         required_size = sizeof(struct ble_store_value_sec);
>         err = nvs_set_blob(nimble_handle, "our_sec", &val->sec, 
> required_size);
>
>         /* NVS Commit */
>         err = nvs_commit(nimble_handle);
>         if (err != ESP_OK) return err;
>         /* NVS Close */
>         nvs_close(nimble_handle);
>
>         return 0;//
>
> Regards
>
> Prasad
>