nRF Device provisioning

The nRF Device provisioning library enables a device to connect to nRF Cloud Provisioning Service, part of nRF Cloud Security Services. It abstracts and hides the details of the transport and encoding scheme that are used for the payload. The current implementation supports the following technologies:

  • AT-command based provisioning commands

  • Writing key-value pair based settings to the Settings storage

  • TLS-secured HTTP as the communication protocol

  • DTLS-secured CoAP as the communication protocol

  • Client authentication with attestation token

  • Client authentication with JWT token

  • CBOR as the data format

Configuration

To enable the library, set the CONFIG_NRF_PROVISIONING Kconfig option to y.

Configuration options for HTTP

Configuration options for CoAP

Usage

The usage of the nRF Device provisioning library is described in the following sections.

Initialization

Once initialized, the provisioning client runs on its own in the background. The provisioning client can be initialized in one of the following ways:

  • The application calls nrf_provisioning_init(), which starts the client.

  • Set the client to initialize during Zephyr’s system initialization phase. In this case, it is assumed that a network connection has been established in the same phase.

The function uses the following arguments:

  • A pointer to a callback function, which is called when the modem state changes.

  • A pointer that must be called once provisioning is done.

If you provide null as a callback function address argument, a corresponding default callback is used. Subsequent calls to the initialization function will only change the callback functions. This behavior is beneficial when the client has been initialized during the system initialization phase, but the application wants to register its own callback functions afterwards.

Provisioning

By default, when provisioning is done after receiving the FINISHED command, the device is rebooted. The behavior can be overwritten by providing a unique callback function for the initialization function.

If anything is written to the modem’s non-volatile memory, the modem needs to be set in offline mode. This is because the modem cannot be connected while any data is being written to its storage area. Once the memory write is complete, the aforementioned callback function must be called again to set the modem to the desired state. To use the default implementation, NULL can be passed as an argument to the nrf_provisioning_init() function. Copy and modify the default callback function as necessary.

The library starts provisioning when it initializes, then according to the configured interval. The interval is read from the storage settings and can be updated with a provisioning command like any other key-value pair.

During provisioning, the library first tries to establish the transport for communicating with the service. This procedure involves a TLS handshake where the client establishes the correct server. The server uses the attestation token or JWT generated by the device to authenticate the client. See Modem attestation token and Modem JWT for more information on client authentication.

The TLS handshake happens twice:

  • Before requesting commands.

  • After the execution of the commands, to report the results.

If you are using AT commands, the library shuts down the modem for writing data to the modem’s non-volatile memory. Once the memory writes are complete, the connection is re-established to report the results back to the server. The results are reported back to the server when either all the commands succeed or when an error occurs. If an error occurs, the results of all the commands that are successfully executed before the error and the erroneous result are reported back to the server. All successfully executed commands will be removed from the server-side queue, but if any errors occur, the erroneous command and all the remaining unexecuted commands are removed from the server-side queue. The log contains more information about the issue.

Immediate provisioning can be requested by calling the nrf_provisioning_trigger_manually() function. Otherwise, the library attempts provisioning according to the set interval. To trigger immediate provisioning, the library must be initialized first.

The following message sequence chart shows a successful provisioning sequence:

msc {
hscale = "1.5";
Owner,Server,Device;
Owner>>Server     [label="Provision: cmd1, cmd2, finished"];
Server<<Device    [label="Get commands"];
Server>>Device    [label="Return commands"];
Device box Device [label="Decode commands"];
Device box Device [label="Set modem offline"];
Device box Device [label="Write to non-volatile memory"];
Device box Device [label="Restore modem state"];
Server<<Device    [label="cmd1,cmd2, finished succeeded"];
}

The following message sequence chart shows a failing provisioning sequence:

msc {
hscale = "1.5";
Owner,Server,Device;
Owner>>Server     [label="Provision: cmd1, cmd2, cmd3, finished"];
Server<<Device    [label="Get commands"];
Server>>Device    [label="Return commands"];
Device box Device [label="Decode commands"];
Device box Device [label="Set modem offline"];
Device box Device [label="cmd1: Write to non-volatile memory"];
Device box Device [label="cmd2: Fails"];
Device box Device [label="Restore modem state"];
Server<<Device    [label="cmd1 success, cmd2 failed"];
Server>>Server    [label="Empty the command queue"];
Server>>Owner     [label="cmd2 failed"];
}

nRF Provisioning shell

To test the client, you can enable Zephyr’s shell and provisioning command, which allow you to control the client over UART. The feature is enabled by selecting CONFIG_NRF_PROVISIONING_SHELL.

Note

The shell is meant for testing. Do not enable it in production.

uart:~$ nrf_provisioning
nrf_provisioning - nRF Provisioning commands
Subcommands:
  init   :Start the client
  now    :Do provisioning now
  token  :Get the attestation token
  uuid   :Get device UUID

Dependencies

This library uses the following nRF Connect SDK libraries:

It uses the following sdk-nrfxlib library:

It uses the following Zephyr libraries:

API documentation

Header file: include/net/nrf_provisioning.h
Source files: subsys/net/lib/nrf_provisioning/src/
group nrf_provisioning

Typedefs

typedef int (*nrf_provisioning_mmode_cb_t)(enum lte_lc_func_mode new_mode, void *user_data)

Callback to request a modem state change, being it powering off, flight mode etc.

Param new_mode:

New mode.

Param user_data:

Not used.

Return:

<0 on error, previous mode on success.

typedef void (*nrf_provisioning_dmode_cb_t)(void *user_data)

Once provisioning is done this callback is to be called.

Return:

<0 on error, previous mode on success.

Functions

int nrf_provisioning_init(struct nrf_provisioning_mm_change *mmode, struct nrf_provisioning_dm_change *dmode)

Initializes the provisioning library and registers callback handlers.

Consequent calls will only change callback functions used. Feeding a null as a callback address means that the corresponding default callback function is taken into use.

Parameters:
  • mmode – Modem mode change callback. Used when data is written to modem.

  • dmode – Provisioning done callback. Used when all provisioning commands have been executed and responded to successfully.

Returns:

<0 on error, 0 on success.

int nrf_provisioning_trigger_manually(void)

Starts provisioning immediately.

Return values:
  • -EFAULT – if the library is uninitialized.

  • 0 – on success.

struct nrf_provisioning_mm_change
#include <nrf_provisioning.h>

Holds the callback used for querying permission from the app to proceed when modem’s state changes. Together with data set by the callback provider.

Param cb:

The callback function.

Param user_data:

App specific data to be fed to the callback once it’s called.

struct nrf_provisioning_dm_change
#include <nrf_provisioning.h>

Holds the callback to be called once provisioning is done together with data set by the callback provider.

Param cb:

The callback function.

Param user_data:

App specific data to be fed to the callback once it’s called.