Hello,
we try to add DTLS to the CoAP client example.
We have problems with the DTLS connection. The handshake with PSK works. The first CON or NON query works also without problems. (See on the attached picture) However, the following requests can no longer be read from the server.
That's why we have watched the data transfer with wireshark. After we registered the PSK in Wireshark the first request/answer was decrypted. The other requests could not be decrypted. Therefore the question why did the key change?
A test setup with a CoAP Server + Cient from Californium does not show this behavior! All data is encrypted here with the same key. How must the configuration be adjusted so that the encryption behaves normally?
The modified Code:
/* * Copyright (c) 2019 Nordic Semiconductor ASA * * SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic */ #include <logging/log.h> LOG_MODULE_REGISTER(coap_client, LOG_LEVEL_DBG); #include "config.h" #include "nrf_inbuilt_key.h" #include <stdio.h> #include <string.h> #include <uart.h> #include <zephyr.h> #include <lte_lc.h> #include <net/coap_api.h> #include <net/socket.h> #define APP_COAP_SEND_INTERVAL_MS K_MSEC(10000) #define APP_COAP_TICK_INTERVAL_MS K_MSEC(1000) struct pollfd fds; static struct sockaddr_storage server; static u16_t next_token; static int transport_handle; coap_transport_init_t transport_param = { .port_table = NULL}; const struct sockaddr_in client_addr = { .sin_port = 0, .sin_family = AF_INET, .sin_addr.s_addr = 0 }; coap_sec_config_t dtls_conf; #define MY_SEC_TAG 101 static sec_tag_t m_sec_tags[] = { MY_SEC_TAG }; int dtls_init(void) { int err; /* Delete certificates */ nrf_sec_tag_t sec_tag = MY_SEC_TAG; for (nrf_key_mgnt_cred_type_t type = 0; type < 5; type++) { err = nrf_inbuilt_key_delete(sec_tag, type); } // Provision Client PSK. err = nrf_inbuilt_key_write(MY_SEC_TAG, NRF_KEY_MGMT_CRED_TYPE_PSK, CLIENT_PSK, sizeof(CLIENT_PSK) - 1); if (err) { printk("CLIENT_PSK error!\n"); return err; } // Provision Client Identity. err = nrf_inbuilt_key_write(MY_SEC_TAG, NRF_KEY_MGMT_CRED_TYPE_IDENTITY, CLIENT_PSK_ID, sizeof(CLIENT_PSK_ID) - 1); if (err) { printk("CLIENT_PSK error!\n"); return err; } return 0; } #if defined(CONFIG_BSD_LIBRARY) /**@brief Recoverable BSD library error. */ void bsd_recoverable_error_handler(uint32_t err) { printk("bsdlib recoverable error: %u\n", err); } /**@brief Irrecoverable BSD library error. */ void bsd_irrecoverable_error_handler(uint32_t err) { printk("bsdlib irrecoverable error: %u\n", err); __ASSERT_NO_MSG(false); } #endif /* defined(CONFIG_BSD_LIBRARY) */ static void inet_str(u32_t value, u8_t *out) { out[3] = (u8_t)(((((u32_t)value) & 0xFF000000) >> 24) & 0x000000FF); out[2] = (u8_t)(((((u32_t)value) & 0x00FF0000) >> 16) & 0x000000FF); out[1] = (u8_t)(((((u32_t)value) & 0x0000FF00) >> 8) & 0x000000FF); out[0] = (u8_t)(((((u32_t)value) & 0x000000FF) >> 0) & 0x000000FF); } /**@brief Handles an error notified by CoAP. */ static void coap_client_error_handler(u32_t error_code, coap_message_t *message) { ARG_UNUSED(message); printk("CoAP error handler: error_code: %u\n", error_code); } /**@brief Resolves the configured hostname. */ static int server_resolve(void) { int err; u8_t ip_str[4]; struct addrinfo *result; struct addrinfo hints = { .ai_family = AF_INET, .ai_socktype = SOCK_DGRAM}; err = getaddrinfo(CONFIG_COAP_SERVER_HOSTNAME, NULL, &hints, &result); if (err != 0) { printk("ERROR: getaddrinfo failed %d\n", err); return -EIO; } if (result == NULL) { printk("ERROR: Address not found\n"); return -ENOENT; } /* IPv4 Address. */ struct sockaddr_in *server4 = ((struct sockaddr_in *)&server); server4->sin_addr.s_addr = ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr; server4->sin_family = AF_INET; server4->sin_port = htons(CONFIG_COAP_SERVER_PORT); //printk("IPv4 Address found 0x%08x\n", server4->sin_addr.s_addr); inet_str(server4->sin_addr.s_addr, ip_str); printk("IPv4 Address found: %d.%d.%d.%d\n", ip_str[0], ip_str[1], ip_str[2], ip_str[3]); /* Free the address. */ freeaddrinfo(result); return 0; } /**@brief Initialize the CoAP client */ static int client_init(void) { int err; dtls_conf.peer_verify = 0; dtls_conf.cipher_count = 0; dtls_conf.cipher_list = NULL; dtls_conf.sec_tag_count = ARRAY_SIZE(m_sec_tags); dtls_conf.sec_tag_list = m_sec_tags; dtls_conf.hostname = CONFIG_COAP_SERVER_HOSTNAME; dtls_conf.role = 0; coap_local_t local_port_list[] = { { .addr = (struct sockaddr *)&client_addr, .protocol = IPPROTO_DTLS_1_2, .setting = &dtls_conf } }; transport_param.port_table = local_port_list; err = coap_init(sys_rand32_get(), &transport_param, k_malloc, k_free); if (err != 0) { printf("Failed to initialize CoAP %d\n", err); return -err; } err = coap_error_handler_register(coap_client_error_handler); if (err != 0) { printf("Failed to register CoAP error handler\n"); return -err; } next_token = sys_rand32_get(); //DTLS Handshake err = coap_security_setup(transport_param.port_table, (struct sockaddr *)&server); if (err != 0) { printf("DTLS Handshake init failed %d\n", err); } else { printf("Send DTLS Handshake\n"); } //Set the DTLS transport_handle /* NOTE: transport_handle is the socket descriptor. */ transport_handle = local_port_list[0].transport; fds.fd = transport_handle; fds.events = POLLIN; return 0; } /**@brief Handles responses from the remote CoAP server. */ static void client_get_response_handle(u32_t status, void *arg, coap_message_t *response) { char buf[15]; printk("CoAP response: status: 0x%x", status); if (status == 0) { snprintf(buf, MAX(response->payload_len, sizeof(buf)), "%s", response->payload); printk(", token 0x%02x%02x, payload: %s", response->token[0], response->token[1], buf); } printk("\n"); } /**@biref Send CoAP GET request. */ static int client_get_send(void) { int err; u32_t handle; coap_message_t *request; coap_message_conf_t message_conf; memset(&message_conf, 0x00, sizeof(message_conf)); message_conf.type = COAP_TYPE_NON; message_conf.code = COAP_CODE_GET; message_conf.transport = transport_handle; message_conf.id = 0; /* Auto-generate message ID. */ message_conf.token[0] = (next_token >> 8) & 0xFF; message_conf.token[1] = next_token & 0xFF; message_conf.token_len = 2; message_conf.response_callback = client_get_response_handle; next_token++; err = coap_message_new(&request, &message_conf); if (err != 0) { printk("Failed to allocate CoAP request message %d!\n", err); return -err; } err = coap_message_remote_addr_set(request, (struct sockaddr *)&server); if (err != 0) { printk("Failed to set coap remote addr %d!\n", err); goto exit; } err = coap_message_opt_str_add(request, COAP_OPT_URI_PATH, (u8_t *)CONFIG_COAP_RESOURCE, strlen(CONFIG_COAP_RESOURCE)); if (err != 0) { printk("Failed to add coap opt to message %d!\n", err); goto exit; } err = coap_message_send(&handle, request); if (err != 0) { printk("Failed to send coap message %d!\n", err); goto exit; } printk("CoAP request sent: token 0x%02x%02x\n", request->token[0], request->token[1]); exit: (void)coap_message_delete(request); return -err; } /**@brief Configures modem to provide LTE link. Blocks until link is * successfully established. */ static void modem_configure(void) { #if defined(CONFIG_LTE_LINK_CONTROL) if (IS_ENABLED(CONFIG_LTE_AUTO_INIT_AND_CONNECT)) { /* Do nothing, modem is already turned on * and connected. */ } else { int err; printk("LTE Link Connecting ...\n"); err = lte_lc_init_and_connect(); __ASSERT(err == 0, "LTE link could not be established."); printk("LTE Link Connected!\n"); } #endif } /* Returns 0 if data is available. * Returns -EAGAIN if timeout occured and there is no data. * Returns other, negative error code in case of poll error. */ static int wait(int timeout) { int ret = poll(&fds, 1, timeout); if (ret < 0) { printk("poll error: %d\n", errno); return -errno; } if (ret == 0) { /* Timeout. */ return -EAGAIN; } if ((fds.revents & POLLERR) == POLLERR) { printk("wait: POLLERR\n"); return -EIO; } if ((fds.revents & POLLNVAL) == POLLNVAL) { printk("wait: POLLNVAL\n"); return -EBADF; } if ((fds.revents & POLLIN) != POLLIN) { return -EAGAIN; } return 0; } void main(void) { int err; printk("The DTLS CoAP client sample started\n"); /* this only needs to be called when changes to PSK are made */ err = dtls_init(); if (err) { printk("DTLS Init Error: %d\n", err); } modem_configure(); if (server_resolve() != 0) { printk("Failed to resolve server name\n"); return; } if (client_init() != 0) { printk("Failed to initialize CoAP client\n"); return; } s64_t next_msg_time = k_uptime_get() + APP_COAP_SEND_INTERVAL_MS; s64_t next_tick_time = k_uptime_get() + APP_COAP_TICK_INTERVAL_MS; while (1) { if (k_uptime_get() >= next_tick_time) { coap_time_tick(); next_tick_time = k_uptime_get() + APP_COAP_TICK_INTERVAL_MS; } if (k_uptime_get() >= next_msg_time) { if (client_get_send() != 0) { printk("Failed to send GET request.\n"); } next_msg_time = k_uptime_get() + APP_COAP_SEND_INTERVAL_MS; } s64_t remaining = next_tick_time - k_uptime_get(); if (remaining < 0) { remaining = 0; } err = wait(remaining); if (err < 0) { if (err == -EAGAIN) { continue; } return; } coap_input(); /* A workaround for incomplete bsd_os_timedwait implementation. */ k_sleep(K_MSEC(10)); } }
Additional information: the code was tested with v0.4.0-rc1 and other untagged releases.
kindest regards
Dennis B.