In this exercise, we will encrypt the communication between our board and the CoAP server using DTLS.
DTLS is based on the TLS protocol and is intended to provide the same security guarantees. The main difference is that DTLS uses UDP, which is the transport layer CoAP uses, and is why we are using it in this exercise.
In addition to encryption, which makes sure that the content of your communication cannot be read by third parties along the network path, DTLS also makes sure that the content cannot be altered by third parties along the network path.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l5/l5_e2
of whichever version directory you are using.
2. In the Kconfig
file, define two new configurations COAP_DEVICE_NAME
and COAP_SERVER_PSK
.
Both these configurations will be used when writing credentials to the modem.
2.1 COAP_DEVICE_NAME
will be used as the PSK Identity. From the documentation for the server we are using, the PSK Identity can be on the form cali.*.*
, so we are using cali.test.nrf91
.
config COAP_DEVICE_NAME
string "Device resource name - this will be the device name on the CoAP server"
default "cali.test.nrf91"
Kconfig2.2 COAP_SERVER_PSK
is the PSK secret, which is the string .fornium
which when converted into hexadecimal values becomes 2e666f726e69756d
.
config COAP_SERVER_PSK
string "Server PSK"
default "2e666f726e69756d"
Kconfig3. Change the configured server port to use the DTLS port for the CoAP server we are using.
In our case, eclipse.californium.io
uses port 5684 for DTLS connections. This is the standard CoAP over DTLS port.
In prj.conf
, change the value of COAP_SERVER_PORT
to be 5684.
CONFIG_COAP_SERVER_PORT=5684
Kconfig4. Enable the modem key management library and TLS credentials API.
4.1 Enable the configuration by adding the following line to the prj.conf
file.
CONFIG_MODEM_KEY_MGMT=y
Kconfig4.2 In main.c
, include the header file for the modem key management library and the TLS credentials API from the BSD socket API.
#include <modem/modem_key_mgmt.h>
#include <zephyr/net/tls_credentials.h>
C5. Define the macro for the security tag.
#define SEC_TAG 12
C6. In client_init()
, create a DTLS socket and use setsockopt()
to write credentials to the socket.
6.1 Create a DTLS socket by changing the last parameter in the call to socket()
to IPPROTO_DTLS_1_2
.
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
C7. Set the DTLS relevant socket options for the socket using setsockopt()
, which has the following signature
sock
– File descriptor for the socket to uselevel
– Specifies at which protocol level the option residesoptname
– A single option to setoptval
– Value of the option to setoptlen
– Length of the option valueWe will be setting the options TLS_PEER_VERIFY
, TLS_HOSTNAME
and TLS_SEC_TAG_LIST
, which resides at the protocol level SOL_TLS
. Note that since DTLS is an implementation of TLS intended to work over datagram sockets, DTLS uses the same socket options as TLS.
7.1 Set the option TLS_PEER_VERIFY
to be required.
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = REQUIRED;
err = setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d\n", errno);
return -errno;
}
C7.2 Set the option TLS_HOSTNAME
to be the hostname for the CoAP server.
err = setsockopt(sock, SOL_TLS, TLS_HOSTNAME, CONFIG_COAP_SERVER_HOSTNAME,
strlen(CONFIG_COAP_SERVER_HOSTNAME));
if (err) {
LOG_ERR("Failed to setup TLS hostname (%s), errno %d\n",
CONFIG_COAP_SERVER_HOSTNAME, errno);
return -errno;
}
C7.3 Set the option TLS_SEC_TAG_LIST
to the value we defined earlier in SEC_TAG
.
When writing the credentials to the modem, this is the security tag that the credentials will be referenced with. The security tag must be attached to the socket before connecting.
sec_tag_t sec_tag_list[] = { SEC_TAG };
err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list,
sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list));
if (err) {
LOG_ERR("Failed to setup socket security tag, errno %d\n", errno);
return -errno;
}
C8. Before calling lte_lc_init_and_connect_async()
, we need to write credentials to the modem using modem_key_mgmt_write()
, which has the following signature
The function takes the security tag associated with the credential as the first parameter. This is the same security tag we wrote to the socket using setsockopt()
.
8.1 First we write the PSK identity to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_IDENTITY
.
This is the value stored in the Kconfig CONFIG_COAP_DEVICE_NAME
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_IDENTITY, CONFIG_COAP_DEVICE_NAME,
strlen(CONFIG_COAP_DEVICE_NAME));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
C8.2 Next, we write the PSK to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_PSK
. Recall that the PSK is stored in the Kconfig CONFIG_COAP_SERVER_PSK
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_PSK, CONFIG_COAP_SERVER_PSK,
strlen(CONFIG_COAP_SERVER_PSK));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
CWe have now configured the application to connect over CoAP using DTLS. However, if we test the application in its current state the connection will disconnect after a certain amount of time.
This is because of something called the NAT session timeout, which is the length of time the network will keep an inactive connection alive. The NAT timeout value is given by the network and can be as low as 12 seconds.
9. To solve this problem, we will set up some logic that will regularly ping the server to keep the connection alive. This will be done using work items and the system workqueue thread.
9.1 Define the interval in which the device will ping the server, TX_KEEP_ALIVE_INTERVAL
.
#define TX_KEEP_ALIVE_INTERVAL 6500
C9.2 Define the delayable work item rx_work
as a structure of type k_work_delayable
.
static struct k_work_delayable rx_work;
C9.3 Define the handler rx_work_fn()
for the work item rx_work
.
Define the function rx_work_fn()
to call client_get_send()
, just to send a package to keep the connection alive.
static void rx_work_fn(struct k_work *work)
{
client_get_send();
}
C9.4 Initialize the work item rx_work
with the handler function rx_work_fn()
.
Before the while
-loop in main()
, call k_work_init_delayable()
to initialize the delayable work structure rx_work
with the handler rx_work_fn()
.
k_work_init_delayable(&rx_work,rx_work_fn);
C9.5 In the while
-loop, reschedule the work item rx_work
using k_work_reschedule()
with a delay of TX_KEEP_ALIVE_INTERVAL
.
k_work_reschedule(&rx_work,K_MSEC(TX_KEEP_ALIVE_INTERVAL));
CSo if we don’t receive anything within the TX_KEEP_ALIVE_INTERVAL
, rx_work
performs the work to keep the connection alive. And if we do receive something, this is handled with a higher priority than rx_work
, and then at the next iteration, rx_work
is rescheduled with a delay of TX_KEEP_ALIVE_INTERVAL
, and the cycle repeats.
10. Build the exercise and flash it to your board.
Testing
11. Let’s first set up a CoAP Client to communicate with our board. This is pretty much similar to the previous exercise. Except you need to specify the server address with coaps .
We will be testing on the PC using cf-browser. You will need Java Runtime Environment installed on your machine.
11.1 Enter the CoAP server URL (make sure it starts with coaps – it is available in the dropdown menu ) and discover its resources as shown below.
11.2 Send a message from the CoAP client to the board.
Locate the CONFIG_COAP_RX_RESOURCE
resource used by the board to receive data. In other words, this is the CoAP resource that you will use to send to the board. This was set in step 3.2 of the previous exercise to validate
. Type the message you want to send to the board and send it as a PUT request. You should see a response of ACK 2.04/CHANGED
which means that the client has successfully modified the content of the resource.
On your board, the board is configured to periodically check this resource and print it on the terminal. Also, pressing button 1 will print the received message on the terminal.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.312,538] <inf> Lesson5_Exercise2: Initializing modem library
[00:00:00.622,528] <inf> Lesson5_Exercise2: Connecting to LTE network
[00:00:04.381,195] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:05.521,118] <inf> Lesson5_Exercise2: Network registration status: Connected - roaming
[00:00:05.521,270] <inf> Lesson5_Exercise2: Connected to LTE network
[00:00:05.688,232] <inf> Lesson5_Exercise2: IPv4 Address found 20.47.97.44
[00:00:05.688,964] <inf> Lesson5_Exercise2: Successfully connected to server
[00:00:10.806,854] <inf> Lesson5_Exercise2: RRC mode: Idle
[00:00:19.563,446] <inf> Lesson5_Exercise2: CoAP GET request sent: Token 0x4a6c
[00:00:19.650,817] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:19.802,947] <inf> Lesson5_Exercise2: CoAP response: Code 0x45, Token 0x4a6c, Payload: Hi from my PC!
[00:00:25.094,940] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalThe payload “Hi From my PC!
” is the value stored in the CONFIG_COAP_RX_RESOURCE
resource.
11.3 Send a message from your board to the CoAP client.
Press button 2 on your nRF91 Series DK, or button 1 twice on the Thingy:91. This will send a PUT request from your board to the CoAP server. The message sent is set in the macro MESSAGE_TO_SEND
in step 4.1 of the previous exercise.
[00:02:55.938,201] <inf> Lesson5_Exercise2: CoAP PUT request sent: Token 0x4a6d
[00:02:56.027,679] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:02:56.207,336] <inf> Lesson5_Exercise2: CoAP response: Code 0x44, Token 0x4a6d, Payload: EMPTY
[00:03:01.572,784] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalNotice that when sending a PUT request, the CoAP packet received back from the server has no payload.
On the CoAP client side, locate the CONFIG_COAP_TX_RESOURCE
resource used by the board to send data. In other words, this is the CoAP resource that you will use to receive from the board. This was set in step 3.2 to large-update
. Then issue a GET request. You should see the message in the response payload as shown below.
The payload “Hi from the nRF91 Series device
” is the value stored in the CONFIG_COAP_TX_RESOURCE
resource.
The solution for this exercise can be found in l5/l5_e2_sol
of whichever version directory you are using.
In this exercise, we will encrypt the communication between our board and the CoAP server using DTLS.
DTLS is based on the TLS protocol and is intended to provide the same security guarantees. The main difference is that DTLS uses UDP, which is the transport layer CoAP uses, and is why we are using it in this exercise.
In addition to encryption, which makes sure that the content of your communication cannot be read by third parties along the network path, DTLS also makes sure that the content cannot be altered by third parties along the network path.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l5/l5_e2
of whichever version directory you are using.
2. In the Kconfig
file, define two new configurations COAP_DEVICE_NAME
and COAP_SERVER_PSK
.
Both these configurations will be used when writing credentials to the modem.
2.1 COAP_DEVICE_NAME
will be used as the PSK Identity. From the documentation for the server we are using, the PSK Identity can be on the form cali.*.*
, so we are using cali.test.nrf91
.
config COAP_DEVICE_NAME
string "Device resource name - this will be the device name on the CoAP server"
default "cali.test.nrf91"
Kconfig2.2 COAP_SERVER_PSK
is the PSK secret, which is the string .fornium
which when converted into hexadecimal values becomes 2e666f726e69756d
.
config COAP_SERVER_PSK
string "Server PSK"
default "2e666f726e69756d"
Kconfig3. Change the configured server port to use the DTLS port for the CoAP server we are using.
In our case, eclipse.californium.io
uses port 5684 for DTLS connections. This is the standard CoAP over DTLS port.
In prj.conf
, change the value of COAP_SERVER_PORT
to be 5684.
CONFIG_COAP_SERVER_PORT=5684
Kconfig4. Enable the modem key management library and TLS credentials API.
4.1 Enable the configuration by adding the following line to the prj.conf
file.
CONFIG_MODEM_KEY_MGMT=y
Kconfig4.2 In main.c
, include the header file for the modem key management library and the TLS credentials API from the BSD socket API.
#include <modem/modem_key_mgmt.h>
#include <zephyr/net/tls_credentials.h>
C5. Define the macro for the security tag.
#define SEC_TAG 12
C6. In client_init()
, create a DTLS socket and use setsockopt()
to write credentials to the socket.
6.1 Create a DTLS socket by changing the last parameter in the call to socket()
to IPPROTO_DTLS_1_2
.
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
C7. Set the DTLS relevant socket options for the socket using setsockopt()
, which has the following signature
sock
– File descriptor for the socket to uselevel
– Specifies at which protocol level the option residesoptname
– A single option to setoptval
– Value of the option to setoptlen
– Length of the option valueWe will be setting the options TLS_PEER_VERIFY
, TLS_HOSTNAME
and TLS_SEC_TAG_LIST
, which resides at the protocol level SOL_TLS
. Note that since DTLS is an implementation of TLS intended to work over datagram sockets, DTLS uses the same socket options as TLS.
7.1 Set the option TLS_PEER_VERIFY
to be required.
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = REQUIRED;
err = setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d\n", errno);
return -errno;
}
C7.2 Set the option TLS_HOSTNAME
to be the hostname for the CoAP server.
err = setsockopt(sock, SOL_TLS, TLS_HOSTNAME, CONFIG_COAP_SERVER_HOSTNAME,
strlen(CONFIG_COAP_SERVER_HOSTNAME));
if (err) {
LOG_ERR("Failed to setup TLS hostname (%s), errno %d\n",
CONFIG_COAP_SERVER_HOSTNAME, errno);
return -errno;
}
C7.3 Set the option TLS_SEC_TAG_LIST
to the value we defined earlier in SEC_TAG
.
When writing the credentials to the modem, this is the security tag that the credentials will be referenced with. The security tag must be attached to the socket before connecting.
sec_tag_t sec_tag_list[] = { SEC_TAG };
err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list,
sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list));
if (err) {
LOG_ERR("Failed to setup socket security tag, errno %d\n", errno);
return -errno;
}
C8. Before calling lte_lc_init_and_connect_async()
, we need to write credentials to the modem using modem_key_mgmt_write()
, which has the following signature
The function takes the security tag associated with the credential as the first parameter. This is the same security tag we wrote to the socket using setsockopt()
.
8.1 First we write the PSK identity to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_IDENTITY
.
This is the value stored in the Kconfig CONFIG_COAP_DEVICE_NAME
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_IDENTITY, CONFIG_COAP_DEVICE_NAME,
strlen(CONFIG_COAP_DEVICE_NAME));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
C8.2 Next, we write the PSK to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_PSK
. Recall that the PSK is stored in the Kconfig CONFIG_COAP_SERVER_PSK
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_PSK, CONFIG_COAP_SERVER_PSK,
strlen(CONFIG_COAP_SERVER_PSK));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
CWe have now configured the application to connect over CoAP using DTLS. However, if we test the application in its current state the connection will disconnect after a certain amount of time.
This is because of something called the NAT session timeout, which is the length of time the network will keep an inactive connection alive. The NAT timeout value is given by the network and can be as low as 12 seconds.
9. To solve this problem, we will set up some logic that will regularly ping the server to keep the connection alive. This will be done using work items and the system workqueue thread.
9.1 Define the interval in which the device will ping the server, TX_KEEP_ALIVE_INTERVAL
.
#define TX_KEEP_ALIVE_INTERVAL 6500
C9.2 Define the delayable work item rx_work
as a structure of type k_work_delayable
.
static struct k_work_delayable rx_work;
C9.3 Define the handler rx_work_fn()
for the work item rx_work
.
Define the function rx_work_fn()
to call client_get_send()
, just to send a package to keep the connection alive.
static void rx_work_fn(struct k_work *work)
{
client_get_send();
}
C9.4 Initialize the work item rx_work
with the handler function rx_work_fn()
.
Before the while
-loop in main()
, call k_work_init_delayable()
to initialize the delayable work structure rx_work
with the handler rx_work_fn()
.
k_work_init_delayable(&rx_work,rx_work_fn);
C9.5 In the while
-loop, reschedule the work item rx_work
using k_work_reschedule()
with a delay of TX_KEEP_ALIVE_INTERVAL
.
k_work_reschedule(&rx_work,K_MSEC(TX_KEEP_ALIVE_INTERVAL));
CSo if we don’t receive anything within the TX_KEEP_ALIVE_INTERVAL
, rx_work
performs the work to keep the connection alive. And if we do receive something, this is handled with a higher priority than rx_work
, and then at the next iteration, rx_work
is rescheduled with a delay of TX_KEEP_ALIVE_INTERVAL
, and the cycle repeats.
10. Build the exercise and flash it to your board.
Testing
11. Let’s first set up a CoAP Client to communicate with our board. This is pretty much similar to the previous exercise. Except you need to specify the server address with coaps .
We will be testing on the PC using cf-browser. You will need Java Runtime Environment installed on your machine.
11.1 Enter the CoAP server URL (make sure it starts with coaps – it is available in the dropdown menu ) and discover its resources as shown below.
11.2 Send a message from the CoAP client to the board.
Locate the CONFIG_COAP_RX_RESOURCE
resource used by the board to receive data. In other words, this is the CoAP resource that you will use to send to the board. This was set in step 3.2 of the previous exercise to validate
. Type the message you want to send to the board and send it as a PUT request. You should see a response of ACK 2.04/CHANGED
which means that the client has successfully modified the content of the resource.
On your board, the board is configured to periodically check this resource and print it on the terminal. Also, pressing button 1 will print the received message on the terminal.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.312,538] <inf> Lesson5_Exercise2: Initializing modem library
[00:00:00.622,528] <inf> Lesson5_Exercise2: Connecting to LTE network
[00:00:04.381,195] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:05.521,118] <inf> Lesson5_Exercise2: Network registration status: Connected - roaming
[00:00:05.521,270] <inf> Lesson5_Exercise2: Connected to LTE network
[00:00:05.688,232] <inf> Lesson5_Exercise2: IPv4 Address found 20.47.97.44
[00:00:05.688,964] <inf> Lesson5_Exercise2: Successfully connected to server
[00:00:10.806,854] <inf> Lesson5_Exercise2: RRC mode: Idle
[00:00:19.563,446] <inf> Lesson5_Exercise2: CoAP GET request sent: Token 0x4a6c
[00:00:19.650,817] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:19.802,947] <inf> Lesson5_Exercise2: CoAP response: Code 0x45, Token 0x4a6c, Payload: Hi from my PC!
[00:00:25.094,940] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalThe payload “Hi From my PC!
” is the value stored in the CONFIG_COAP_RX_RESOURCE
resource.
11.3 Send a message from your board to the CoAP client.
Press button 2 on your nRF91 Series DK, or button 1 twice on the Thingy:91. This will send a PUT request from your board to the CoAP server. The message sent is set in the macro MESSAGE_TO_SEND
in step 4.1 of the previous exercise.
[00:02:55.938,201] <inf> Lesson5_Exercise2: CoAP PUT request sent: Token 0x4a6d
[00:02:56.027,679] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:02:56.207,336] <inf> Lesson5_Exercise2: CoAP response: Code 0x44, Token 0x4a6d, Payload: EMPTY
[00:03:01.572,784] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalNotice that when sending a PUT request, the CoAP packet received back from the server has no payload.
On the CoAP client side, locate the CONFIG_COAP_TX_RESOURCE
resource used by the board to send data. In other words, this is the CoAP resource that you will use to receive from the board. This was set in step 3.2 to large-update
. Then issue a GET request. You should see the message in the response payload as shown below.
The payload “Hi from the nRF91 Series device
” is the value stored in the CONFIG_COAP_TX_RESOURCE
resource.
The solution for this exercise can be found in l5/l5_e2_sol
of whichever version directory you are using.
In this exercise, we will encrypt the communication between our board and the CoAP server using DTLS.
DTLS is based on the TLS protocol and is intended to provide the same security guarantees. The main difference is that DTLS uses UDP, which is the transport layer CoAP uses, and is why we are using it in this exercise.
In addition to encryption, which makes sure that the content of your communication cannot be read by third parties along the network path, DTLS also makes sure that the content cannot be altered by third parties along the network path.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l5/l5_e2
of whichever version directory you are using.
2. In the Kconfig
file, define two new configurations COAP_DEVICE_NAME
and COAP_SERVER_PSK
.
Both these configurations will be used when writing credentials to the modem.
2.1 COAP_DEVICE_NAME
will be used as the PSK Identity. From the documentation for the server we are using, the PSK Identity can be on the form cali.*.*
, so we are using cali.test.nrf91
.
config COAP_DEVICE_NAME
string "Device resource name - this will be the device name on the CoAP server"
default "cali.test.nrf91"
Kconfig2.2 COAP_SERVER_PSK
is the PSK secret, which is the string .fornium
which when converted into hexadecimal values becomes 2e666f726e69756d
.
config COAP_SERVER_PSK
string "Server PSK"
default "2e666f726e69756d"
Kconfig3. Change the configured server port to use the DTLS port for the CoAP server we are using.
In our case, eclipse.californium.io
uses port 5684 for DTLS connections. This is the standard CoAP over DTLS port.
In prj.conf
, change the value of COAP_SERVER_PORT
to be 5684.
CONFIG_COAP_SERVER_PORT=5684
Kconfig4. Enable the modem key management library and TLS credentials API.
4.1 Enable the configuration by adding the following line to the prj.conf
file.
CONFIG_MODEM_KEY_MGMT=y
Kconfig4.2 In main.c
, include the header file for the modem key management library and the TLS credentials API from the BSD socket API.
#include <modem/modem_key_mgmt.h>
#include <zephyr/net/tls_credentials.h>
C5. Define the macro for the security tag.
#define SEC_TAG 12
C6. In client_init()
, create a DTLS socket and use setsockopt()
to write credentials to the socket.
6.1 Create a DTLS socket by changing the last parameter in the call to socket()
to IPPROTO_DTLS_1_2
.
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_DTLS_1_2);
C7. Set the DTLS relevant socket options for the socket using setsockopt()
, which has the following signature
sock
– File descriptor for the socket to uselevel
– Specifies at which protocol level the option residesoptname
– A single option to setoptval
– Value of the option to setoptlen
– Length of the option valueWe will be setting the options TLS_PEER_VERIFY
, TLS_HOSTNAME
and TLS_SEC_TAG_LIST
, which resides at the protocol level SOL_TLS
. Note that since DTLS is an implementation of TLS intended to work over datagram sockets, DTLS uses the same socket options as TLS.
7.1 Set the option TLS_PEER_VERIFY
to be required.
enum {
NONE = 0,
OPTIONAL = 1,
REQUIRED = 2,
};
int verify = REQUIRED;
err = setsockopt(sock, SOL_TLS, TLS_PEER_VERIFY, &verify, sizeof(verify));
if (err) {
LOG_ERR("Failed to setup peer verification, errno %d\n", errno);
return -errno;
}
C7.2 Set the option TLS_HOSTNAME
to be the hostname for the CoAP server.
err = setsockopt(sock, SOL_TLS, TLS_HOSTNAME, CONFIG_COAP_SERVER_HOSTNAME,
strlen(CONFIG_COAP_SERVER_HOSTNAME));
if (err) {
LOG_ERR("Failed to setup TLS hostname (%s), errno %d\n",
CONFIG_COAP_SERVER_HOSTNAME, errno);
return -errno;
}
C7.3 Set the option TLS_SEC_TAG_LIST
to the value we defined earlier in SEC_TAG
.
When writing the credentials to the modem, this is the security tag that the credentials will be referenced with. The security tag must be attached to the socket before connecting.
sec_tag_t sec_tag_list[] = { SEC_TAG };
err = setsockopt(sock, SOL_TLS, TLS_SEC_TAG_LIST, sec_tag_list,
sizeof(sec_tag_t) * ARRAY_SIZE(sec_tag_list));
if (err) {
LOG_ERR("Failed to setup socket security tag, errno %d\n", errno);
return -errno;
}
C8. Before calling lte_lc_init_and_connect_async()
, we need to write credentials to the modem using modem_key_mgmt_write()
, which has the following signature
The function takes the security tag associated with the credential as the first parameter. This is the same security tag we wrote to the socket using setsockopt()
.
8.1 First we write the PSK identity to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_IDENTITY
.
This is the value stored in the Kconfig CONFIG_COAP_DEVICE_NAME
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_IDENTITY, CONFIG_COAP_DEVICE_NAME,
strlen(CONFIG_COAP_DEVICE_NAME));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
C8.2 Next, we write the PSK to the modem, by specifying the credential type MODEM_KEY_MGMT_CRED_TYPE_PSK
. Recall that the PSK is stored in the Kconfig CONFIG_COAP_SERVER_PSK
.
err = modem_key_mgmt_write(SEC_TAG, MODEM_KEY_MGMT_CRED_TYPE_PSK, CONFIG_COAP_SERVER_PSK,
strlen(CONFIG_COAP_SERVER_PSK));
if (err) {
LOG_ERR("Failed to write identity: %d\n", err);
return err;
}
CWe have now configured the application to connect over CoAP using DTLS. However, if we test the application in its current state the connection will disconnect after a certain amount of time.
This is because of something called the NAT session timeout, which is the length of time the network will keep an inactive connection alive. The NAT timeout value is given by the network and can be as low as 12 seconds.
9. To solve this problem, we will set up some logic that will regularly ping the server to keep the connection alive. This will be done using work items and the system workqueue thread.
9.1 Define the interval in which the device will ping the server, TX_KEEP_ALIVE_INTERVAL
.
#define TX_KEEP_ALIVE_INTERVAL 6500
C9.2 Define the delayable work item rx_work
as a structure of type k_work_delayable
.
static struct k_work_delayable rx_work;
C9.3 Define the handler rx_work_fn()
for the work item rx_work
.
Define the function rx_work_fn()
to call client_get_send()
, just to send a package to keep the connection alive.
static void rx_work_fn(struct k_work *work)
{
client_get_send();
}
C9.4 Initialize the work item rx_work
with the handler function rx_work_fn()
.
Before the while
-loop in main()
, call k_work_init_delayable()
to initialize the delayable work structure rx_work
with the handler rx_work_fn()
.
k_work_init_delayable(&rx_work,rx_work_fn);
C9.5 In the while
-loop, reschedule the work item rx_work
using k_work_reschedule()
with a delay of TX_KEEP_ALIVE_INTERVAL
.
k_work_reschedule(&rx_work,K_MSEC(TX_KEEP_ALIVE_INTERVAL));
CSo if we don’t receive anything within the TX_KEEP_ALIVE_INTERVAL
, rx_work
performs the work to keep the connection alive. And if we do receive something, this is handled with a higher priority than rx_work
, and then at the next iteration, rx_work
is rescheduled with a delay of TX_KEEP_ALIVE_INTERVAL
, and the cycle repeats.
10. Build the exercise and flash it to your board.
Testing
11. Let’s first set up a CoAP Client to communicate with our board. This is pretty much similar to the previous exercise. Except you need to specify the server address with coaps .
We will be testing on the PC using cf-browser. You will need Java Runtime Environment installed on your machine.
11.1 Enter the CoAP server URL (make sure it starts with coaps – it is available in the dropdown menu ) and discover its resources as shown below.
11.2 Send a message from the CoAP client to the board.
Locate the CONFIG_COAP_RX_RESOURCE
resource used by the board to receive data. In other words, this is the CoAP resource that you will use to send to the board. This was set in step 3.2 of the previous exercise to validate
. Type the message you want to send to the board and send it as a PUT request. You should see a response of ACK 2.04/CHANGED
which means that the client has successfully modified the content of the resource.
On your board, the board is configured to periodically check this resource and print it on the terminal. Also, pressing button 1 will print the received message on the terminal.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.312,538] <inf> Lesson5_Exercise2: Initializing modem library
[00:00:00.622,528] <inf> Lesson5_Exercise2: Connecting to LTE network
[00:00:04.381,195] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:05.521,118] <inf> Lesson5_Exercise2: Network registration status: Connected - roaming
[00:00:05.521,270] <inf> Lesson5_Exercise2: Connected to LTE network
[00:00:05.688,232] <inf> Lesson5_Exercise2: IPv4 Address found 20.47.97.44
[00:00:05.688,964] <inf> Lesson5_Exercise2: Successfully connected to server
[00:00:10.806,854] <inf> Lesson5_Exercise2: RRC mode: Idle
[00:00:19.563,446] <inf> Lesson5_Exercise2: CoAP GET request sent: Token 0x4a6c
[00:00:19.650,817] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:00:19.802,947] <inf> Lesson5_Exercise2: CoAP response: Code 0x45, Token 0x4a6c, Payload: Hi from my PC!
[00:00:25.094,940] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalThe payload “Hi From my PC!
” is the value stored in the CONFIG_COAP_RX_RESOURCE
resource.
11.3 Send a message from your board to the CoAP client.
Press button 2 on your nRF91 Series DK, or button 1 twice on the Thingy:91. This will send a PUT request from your board to the CoAP server. The message sent is set in the macro MESSAGE_TO_SEND
in step 4.1 of the previous exercise.
[00:02:55.938,201] <inf> Lesson5_Exercise2: CoAP PUT request sent: Token 0x4a6d
[00:02:56.027,679] <inf> Lesson5_Exercise2: RRC mode: Connected
[00:02:56.207,336] <inf> Lesson5_Exercise2: CoAP response: Code 0x44, Token 0x4a6d, Payload: EMPTY
[00:03:01.572,784] <inf> Lesson5_Exercise2: RRC mode: Idle
TerminalNotice that when sending a PUT request, the CoAP packet received back from the server has no payload.
On the CoAP client side, locate the CONFIG_COAP_TX_RESOURCE
resource used by the board to send data. In other words, this is the CoAP resource that you will use to receive from the board. This was set in step 3.2 to large-update
. Then issue a GET request. You should see the message in the response payload as shown below.
The payload “Hi from the nRF91 Series device
” is the value stored in the CONFIG_COAP_TX_RESOURCE
resource.
The solution for this exercise can be found in l5/l5_e2_sol
of whichever version directory you are using.