From 4eb3ef4b4f3bb9cabd61a9d5dbc7c4546103b52c Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Thu, 11 Mar 2021 15:12:27 +0100 Subject: [PATCH 1/8] pkg/wakaama: add own security object implementation --- pkg/wakaama/Kconfig | 109 +-- pkg/wakaama/Makefile.dep | 3 +- pkg/wakaama/contrib/lwm2m_client_objects.c | 26 +- pkg/wakaama/contrib/objects/Kconfig | 1 + pkg/wakaama/contrib/objects/Kconfig.security | 26 + pkg/wakaama/contrib/objects/security.c | 913 +++++++++++++++++++ pkg/wakaama/doc.txt | 8 +- pkg/wakaama/include/lwm2m_client_config.h | 33 +- pkg/wakaama/include/lwm2m_client_objects.h | 12 - pkg/wakaama/include/objects/security.h | 433 +++++++++ pkg/wakaama/wakaama_client.mk | 1 - 11 files changed, 1422 insertions(+), 143 deletions(-) create mode 100644 pkg/wakaama/contrib/objects/Kconfig.security create mode 100644 pkg/wakaama/contrib/objects/security.c create mode 100644 pkg/wakaama/include/objects/security.h diff --git a/pkg/wakaama/Kconfig b/pkg/wakaama/Kconfig index 4f7b0ca91755..4d265b095f58 100644 --- a/pkg/wakaama/Kconfig +++ b/pkg/wakaama/Kconfig @@ -1,4 +1,4 @@ -# Copyright (c) 2019 HAW Hamburg +# Copyright (c) 2021 HAW Hamburg # # This file is subject to the terms and conditions of the GNU Lesser # General Public License v2.1. See the file LICENSE in the top level @@ -10,96 +10,15 @@ menu "Wakaama LwM2M" menu "Remote server" config LWM2M_STANDARD_PORT - string "CoAP default port of the LwM2M server" + string "CoAP default port of a LwM2M server" default "5683" config LWM2M_DTLS_PORT - string "CoAPS default port of the LwM2M server" + string "CoAPS default port of a LwM2M server" default "5684" -config LWM2M_SERVER_URI - string "LwM2M server URI to register/bootstrap with" - default "coap://[fd00:dead:beef::1]" - help - The host part of the URI MUST be a valid IPv6 address. Host names can - not be resolved at this time. - -config LWM2M_SERVER_ID - int "Numeric ID of the Server URI" - default 10 - help - This numeric ID corresponds to the server URI specified in the - previous option. - -config LWM2M_BOOTSTRAP - bool "Is a bootstrap server" - help - The specified server is a LwM2M bootstrap server. - endmenu # Remote server -menu "Device" - -choice - bool "Device binding and queue mode" - default LWM2M_DEVICE_BINDING_U - -config LWM2M_DEVICE_BINDING_U - bool "UDP" - -config LWM2M_DEVICE_BINDING_UQ - bool "UDP with Queue mode" - -config LWM2M_DEVICE_BINDING_S - bool "SMS" - -config LWM2M_DEVICE_BINDING_SQ - bool "SMS with Queue mode" - -config LWM2M_DEVICE_BINDING_US - bool "UDP and SMS" - -config LWM2M_DEVICE_BINDING_UQS - bool "UDP with Queue mode and SMS" - -endchoice - -config LWM2M_DEVICE_NAME - string "Device name" - default "testRIOTDevice" - help - This is the device name used to register at the LwM2M server. - -config LWM2M_DEVICE_MANUFACTURER - string "Device manufacturer" - default "A RIOT maker" - -config LWM2M_DEVICE_MODEL - string "Device model" - default "$(BOARD)" - -config LWM2M_DEVICE_TYPE - string "Device type" - default "RIOT device" - -config LWM2M_DEVICE_SERIAL - string "Device serial number" - default "undefined" - -config LWM2M_DEVICE_FW_VERSION - string "Device firmware version" - default "" - -config LWM2M_DEVICE_HW_VERSION - string "Device hardware version" - default "$(BOARD)" - -config LWM2M_DEVICE_SW_VERSION - string "Device software version" - default "" - -endmenu # Device - config LWM2M_DEVICE_TTL int "Lifetime of the device" default 300 @@ -107,9 +26,13 @@ config LWM2M_DEVICE_TTL Lifetime of the device on the LwM2M server, expressed in seconds. config LWM2M_LOCAL_PORT - string "Default port for the local LwM2M instance" + string "Port for the local LwM2M CoAP" default "5683" +config LWM2M_LOCAL_DTLS_PORT + string "Port for the local LwM2M CoAPs server" + default "5684" + config LWM2M_ALT_PATH string "Alternate path to place LwM2M resources" default "/" @@ -121,6 +44,22 @@ config LWM2M_TLSF_BUFFER int "Allocation buffer size" default 5120 +config LWM2M_CREDMAN_TAG_BASE + int "Credential tag base" + default 10 + help + Number to use as base for assigning tags to @ref net_credman + credentials. + +config LWM2M_URI_MAX_SIZE + int "Maximum length of an URI allowed" + default 64 + +config LWM2M_BOOTSTRAP + bool "Bootsrap server support" + help + Say y to support using a bootstrap server to get server information. + rsource "contrib/objects/Kconfig" endmenu # Wakaama LwM2M diff --git a/pkg/wakaama/Makefile.dep b/pkg/wakaama/Makefile.dep index b2e701472448..e10f5bf102fc 100644 --- a/pkg/wakaama/Makefile.dep +++ b/pkg/wakaama/Makefile.dep @@ -8,8 +8,9 @@ USEMODULE += uri_parser # folder, by adding 'wakaama_objects_' modules USEMODULE += wakaama_objects -# include the 'device' object implementation (mandatory) +# include mandatory objects USEMODULE += wakaama_objects_device +USEMODULE += wakaama_objects_security USEMODULE += ztimer USEMODULE += ztimer_sec diff --git a/pkg/wakaama/contrib/lwm2m_client_objects.c b/pkg/wakaama/contrib/lwm2m_client_objects.c index aced1c64cc71..e4cd489cf0ac 100644 --- a/pkg/wakaama/contrib/lwm2m_client_objects.c +++ b/pkg/wakaama/contrib/lwm2m_client_objects.c @@ -24,33 +24,13 @@ #include "lwm2m_client_config.h" #include "lwm2m_client_objects.h" -/* These functions are defined by the objects (object_security.c and - * object_server.c are implemented by the Wakaama package. device.c can be - * found in 'contrib/objects') */ -lwm2m_object_t *get_security_object(int server_id, const char *server_uri, - char *bs_psk_id, char *psk, - uint16_t psk_len, bool is_bootstrap); +/* These functions are defined by the objects (object_server.c is implemented by + * the Wakaama package. security.c and device.c can be found in + * 'contrib/objects') */ lwm2m_object_t *get_server_object(int server_id, const char *binding, int lifetime, bool storing); lwm2m_object_t *lwm2m_get_object_device(void); -lwm2m_object_t *lwm2m_client_get_security_object( - lwm2m_client_data_t *client_data) -{ - (void)client_data; - lwm2m_object_t *ret; - char *server_uri = CONFIG_LWM2M_SERVER_URI; - int server_id = CONFIG_LWM2M_SERVER_ID; - uint16_t psk_len = -1; - char *psk_buffer = NULL; - char *psk_id = NULL; - - ret = get_security_object(server_id, server_uri, psk_id, psk_buffer, - psk_len, IS_ACTIVE(CONFIG_LWM2M_BOOTSTRAP)); - - return ret; -} - lwm2m_object_t *lwm2m_client_get_server_object( lwm2m_client_data_t *client_data) { diff --git a/pkg/wakaama/contrib/objects/Kconfig b/pkg/wakaama/contrib/objects/Kconfig index dbd167c552b7..416a0c342aab 100644 --- a/pkg/wakaama/contrib/objects/Kconfig +++ b/pkg/wakaama/contrib/objects/Kconfig @@ -6,3 +6,4 @@ # rsource "Kconfig.light_control" +rsource "Kconfig.security" diff --git a/pkg/wakaama/contrib/objects/Kconfig.security b/pkg/wakaama/contrib/objects/Kconfig.security new file mode 100644 index 000000000000..bc0a161f92e6 --- /dev/null +++ b/pkg/wakaama/contrib/objects/Kconfig.security @@ -0,0 +1,26 @@ +# Copyright (c) 2021 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menu "Security object" + +config LWM2M_OBJ_SECURITY_INSTANCES_MAX + int "Maximum number of instances of the Security object" + default 2 + +config LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE + int "Buffer size of the public key or ID resource" + default 128 + +config LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE + int "Buffer size of the server public key resource" + default 128 + +config LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE + int "Buffer size of the secret key resource" + default 64 + +endmenu # Security object diff --git a/pkg/wakaama/contrib/objects/security.c b/pkg/wakaama/contrib/objects/security.c new file mode 100644 index 000000000000..b4765130b004 --- /dev/null +++ b/pkg/wakaama/contrib/objects/security.c @@ -0,0 +1,913 @@ +/* + * Copyright (C) 2024 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @{ + * @ingroup lwm2m_objects_security + * + * @file + * @brief Security object implementation for LwM2M client using Wakaama + * + * @author Leandro Lanzieri + * @} + */ + +#include "liblwm2m.h" +#include "objects/security.h" +#include "lwm2m_client_config.h" +#include "lwm2m_client.h" +#include "kernel_defines.h" +#include "net/credman.h" +#include "mutex.h" + +#include +#include +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#define _USED_INSTANCES(_obj) (_obj.wakaama_object.instanceList) +#define _FREE_INSTANCES(_obj) (_obj.free_instances) + +/** + * @brief Descriptor of a LwM2M Security object instance (Object ID = 0) + */ +typedef struct lwm2m_obj_security_inst { + /** + * @brief Linked list handle. + */ + lwm2m_list_t list; + + /** + * @brief Server URI. + */ + char uri[CONFIG_LWM2M_URI_MAX_SIZE]; + + /** + * @brief Indicates if the server associated to the security instance is a Bootstrap-Server. + */ + bool is_bootstrap; + + /** + * @brief Security mode to use with the server. + */ + uint8_t security_mode; + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + /** + * @brief Tag of the credential to use with the server. + */ + credman_tag_t cred_tag; + + /** + * @brief Buffer for the public key or ID resource. + */ + uint8_t pub_key_or_id[CONFIG_LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE]; + + /** + * @brief Bytes used in @ref lwm2m_obj_security_inst_t::pub_key_or_id. + */ + size_t pub_key_or_id_len; + + /** + * @brief Buffer for the server public key resource. + */ + uint8_t server_pub_key[CONFIG_LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE]; + + /** + * @brief Bytes used in @ref lwm2m_obj_security_inst_t::server_pub_key. + */ + size_t server_pub_key_len; + + /** + * @brief Credman public key structure for the server. + */ + ecdsa_public_key_t server_credman_pub_key; + + /** + * @brief Buffer for the secret or private key resource. + */ + uint8_t secret_key[CONFIG_LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE]; + + /** + * @brief Bytes used in @ref lwm2m_obj_security_inst_t::secret_key. + */ + size_t secret_key_len; +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + + /** + * @brief Short ID to reference the server. + */ + uint16_t short_id; + + /** + * @brief Hold off time for registration. + */ + uint32_t client_hold_off_time; + + /** + * @brief Timeout for Bootstrap-Server account deletion. + */ + uint32_t bs_account_timeout; +} lwm2m_obj_security_inst_t; + +/** + * @brief 'Read' callback for the security object. + * + * @param[in] instance_id ID of the instance to read + * @param[in, out] num_data Number of resources requested. 0 means all. + * @param[in, out] data_array Initialized data array to output the values, + * when @p num_data != 0. Uninitialized otherwise. + * @param[in] object Security object pointer + * + * @retval COAP_205_CONTENT on success + * @retval COAP_404_NOT_FOUND when resource can't be found + * @retval COAP_500_INTERNAL_SERVER_ERROR otherwise + */ +static uint8_t _read_cb(uint16_t instance_id, int *num_data, lwm2m_data_t *data_array[], + lwm2m_object_t *object); + +/** + * @brief 'Write' callback for the security object. + * + * @param[in] instance_id ID of the instance to write to + * @param[in] num_data Number of resources to write + * @param[in] data_array Array of data to write + * @param[in] object Security object pointer + * + * @retval COAP_204_CHANGED on success + * @retval COAP_400_BAD_REQUEST otherwise + */ +static uint8_t _write_cb(uint16_t instance_id, int num_data, lwm2m_data_t *data_array, + lwm2m_object_t *object); + +/** + * @brief 'Delete' callback for the security object. + * + * @param[in] instance_id ID of the instance to delete + * @param[in] object Security object pointer + * + * @retval COAP_202_DELETED on success + * @retval COAP_404_NOT_FOUND when the instance can't be found + */ +static uint8_t _delete_cb(uint16_t instance_id, lwm2m_object_t *object); + +/** + * @brief 'Create' callback for the security object. + * + * @param[in] instance_id ID of the instance to create + * @param[in] num_data Number of resources to write + * @param[in] data_array Array of data to write + * @param[in] object Security object pointer + * + * @retval COAP_201_CREATED on success + * @retval COAP_500_INTERNAL_SERVER_ERROR otherwise + */ +static uint8_t _create_cb(uint16_t instance_id, int num_data, lwm2m_data_t *data_array, + lwm2m_object_t *object); + +/** + * @brief Get a value from a security object instance. + * + * @param data[in, out] Data structure indicating the id of the resource + * to get the value of. It will contain the value if + * successful. + * @param instance[in] Instance to get the data from. + * @retval 0 on success + * @retval <0 otherwise + */ +static int _get_value(lwm2m_data_t *data, lwm2m_obj_security_inst_t *instance); + +/** + * @brief Initialize a new instance with the given arguments. + * + * @param instance[out] Instance to initialize. + * @param instance_id[in] ID of the instance. + * @param args[in] Arguments to initialize the instance with. + * + * @retval 0 on success + * @retval -ENOMEM if there is no memory available to copy credentials + * @retval -EINVAL if the arguments are invalid + */ +static int _initialize_new_instance(lwm2m_obj_security_inst_t *instance, uint16_t instance_id, + const lwm2m_obj_security_args_t *args); + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +/** + * @brief Update a credential in the credman registry with the current instance information. + * + * @param[in] instance Instance to update the credential to. + * + * @retval 0 on success + * @retval <0 otherwise + */ +static int _update_credential(lwm2m_obj_security_inst_t *instance); +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + +struct lwm2m_security_object { + lwm2m_object_t wakaama_object; /**< Wakaama internal object */ + mutex_t lock; /**< mutex for the instances access */ + lwm2m_obj_security_inst_t *free_instances; /**< list of free instances */ + lwm2m_obj_security_inst_t instances[CONFIG_LWM2M_OBJ_SECURITY_INSTANCES_MAX]; /**< instances */ +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + credman_tag_t tag_count; /**< counter for the credential tags */ +#endif +}; + +/** + * @brief Implementation of the object interface for the Security Object. + */ +static struct lwm2m_security_object _security_object = { + .lock = MUTEX_INIT, +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + .tag_count= CONFIG_LWM2M_CREDMAN_TAG_BASE, +#endif + .wakaama_object = { + .next = NULL, + .objID = LWM2M_SECURITY_OBJECT_ID, + .instanceList = NULL, + .readFunc = _read_cb, + .writeFunc = _write_cb, + .createFunc = _create_cb, + .deleteFunc = _delete_cb, + .executeFunc = NULL, + .discoverFunc = NULL, + .userData = NULL, + } +}; + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +static int _update_credential(lwm2m_obj_security_inst_t *instance) +{ + assert(instance); + + credman_credential_t cred; + credman_type_t type; + + if (instance->security_mode == LWM2M_SECURITY_MODE_PRE_SHARED_KEY) { + type = CREDMAN_TYPE_PSK; + } + else { + type = CREDMAN_TYPE_ECDSA; + } + + credman_delete(instance->cred_tag, type); + + if (instance->cred_tag == CREDMAN_TAG_EMPTY) { + instance->cred_tag = ++_security_object.tag_count; + } + + cred.type = type; + cred.tag = instance->cred_tag; + + DEBUG("[lwm2m:security]: updating credential with tag %d\n", cred.tag); + if (type == CREDMAN_TYPE_PSK) { + DEBUG("[lwm2m:security]: PSK ID: %.*s\n", (unsigned)instance->pub_key_or_id_len, instance->pub_key_or_id); + DEBUG("[lwm2m:security]: PSK Key: %.*s\n", (unsigned)instance->secret_key_len, instance->secret_key); + + cred.params.psk.id.s = instance->pub_key_or_id; + cred.params.psk.id.len = instance->pub_key_or_id_len; + cred.params.psk.key.s = instance->secret_key; + cred.params.psk.key.len = instance->secret_key_len; + } + else { + DEBUG("[lwm2m:security]: Server pub. key: ["); + for (size_t i = 0; i < instance->server_pub_key_len; i++) { + DEBUG("0x%02x ", instance->server_pub_key[i]); + } + DEBUG("]\n"); + + DEBUG("[lwm2m:security]: Pub. key: ["); + for (size_t i = 0; i < instance->pub_key_or_id_len; i++) { + DEBUG("0x%02x ", instance->pub_key_or_id[i]); + } + DEBUG("]\n"); + + DEBUG("[lwm2m:security]: Priv. key: ["); + for (size_t i = 0; i < instance->secret_key_len; i++) { + DEBUG("0x%02x ", instance->secret_key[i]); + } + DEBUG("]\n"); + + int res = credman_load_public_key(instance->server_pub_key, instance->server_pub_key_len, + &instance->server_credman_pub_key); + if (res != CREDMAN_OK) { + DEBUG("[lwm2m:security]: error loading server public key (%d)\n", res); + return -1; + } + + cred.params.ecdsa.client_keys = &instance->server_credman_pub_key; + cred.params.ecdsa.client_keys_size = 1; + + res = credman_load_private_ecc_key(instance->secret_key, instance->secret_key_len, &cred); + if (res != CREDMAN_OK) { + DEBUG("[lwm2m:security]: error loading private key (%d)\n", res); + return -1; + } + + res = credman_load_public_key(instance->pub_key_or_id, instance->pub_key_or_id_len, + &(cred.params.ecdsa.public_key)); + if (res != CREDMAN_OK) { + DEBUG("[lwm2m:security]: error loading own public key (%d)\n", res); + return -1; + } + } + + lwm2m_client_add_credential(instance->cred_tag); + + return credman_add(&cred) == CREDMAN_OK ? 0 : -1; +} +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + +static int _get_value(lwm2m_data_t *data, lwm2m_obj_security_inst_t *instance) +{ + assert(data); + assert(instance); + + /* resource IDs are defined by Wakaama in liblwm2m.h */ + switch (data->id) { + case LWM2M_SECURITY_URI_ID: + lwm2m_data_encode_string(instance->uri, data); + break; + + case LWM2M_SECURITY_BOOTSTRAP_ID: + lwm2m_data_encode_bool(instance->is_bootstrap, data); + break; + + case LWM2M_SECURITY_SHORT_SERVER_ID: + lwm2m_data_encode_int(instance->short_id, data); + break; + + case LWM2M_SECURITY_HOLD_OFF_ID: + lwm2m_data_encode_int(instance->client_hold_off_time, data); + break; + + case LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID: + lwm2m_data_encode_int(instance->bs_account_timeout, data); + break; + + case LWM2M_SECURITY_SECURITY_ID: + lwm2m_data_encode_int(instance->security_mode, data); + break; + + case LWM2M_SECURITY_PUBLIC_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + lwm2m_data_encode_opaque(instance->pub_key_or_id, instance->pub_key_or_id_len, data); +#else + return COAP_404_NOT_FOUND; +#endif + break; + + case LWM2M_SECURITY_SECRET_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + lwm2m_data_encode_opaque(instance->secret_key, instance->secret_key_len, data); +#else + return COAP_404_NOT_FOUND; +#endif + break; + + case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + lwm2m_data_encode_opaque(instance->server_pub_key, instance->server_pub_key_len, data); +#else + return COAP_404_NOT_FOUND; +#endif + break; + + /* not implemented */ + case LWM2M_SECURITY_SMS_SECURITY_ID: + case LWM2M_SECURITY_SMS_KEY_PARAM_ID: + case LWM2M_SECURITY_SMS_SECRET_KEY_ID: + case LWM2M_SECURITY_SMS_SERVER_NUMBER_ID: + return COAP_404_NOT_FOUND; + + default: + return COAP_404_NOT_FOUND; + } + return COAP_205_CONTENT; +} + +static uint8_t _read_cb(uint16_t instance_id, int *num_data, lwm2m_data_t *data_array[], + lwm2m_object_t *object) +{ + lwm2m_obj_security_inst_t *instance; + uint8_t result; + int i = 0; + + /* try to get the requested instance from the object list */ + instance = (lwm2m_obj_security_inst_t *)lwm2m_list_find(object->instanceList, instance_id); + if (NULL == instance) { + result = COAP_404_NOT_FOUND; + goto out; + } + + /* if the number of resources is not specified, we need to read all resources */ + if (!*num_data) { + DEBUG("[security:read] all resources are read\n"); + + const uint16_t resList[] = { + LWM2M_SECURITY_URI_ID, + LWM2M_SECURITY_BOOTSTRAP_ID, + LWM2M_SECURITY_SECURITY_ID, +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + LWM2M_SECURITY_PUBLIC_KEY_ID, + LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID, + LWM2M_SECURITY_SECRET_KEY_ID, +#endif + /* LWM2M_SECURITY_SMS_SECURITY_ID, */ + /* LWM2M_SECURITY_SMS_KEY_PARAM_ID, */ + /* LWM2M_SECURITY_SMS_SECRET_KEY_ID, */ + /* LWM2M_SECURITY_SMS_SERVER_NUMBER_ID, */ + LWM2M_SECURITY_SHORT_SERVER_ID, + LWM2M_SECURITY_HOLD_OFF_ID, + LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID + }; + + /* try to allocate data structures for all resources */ + int resNum = ARRAY_SIZE(resList); + *data_array = lwm2m_data_new(resNum); + + if (NULL == *data_array) { + result = COAP_500_INTERNAL_SERVER_ERROR; + goto out; + } + + /* indicate the number of resources that are returned */ + *num_data = resNum; + + /* prepare the resource ID of all resources for the request */ + for (i = 0 ; i < resNum ; i++) { + (*data_array)[i].id = resList[i]; + } + } + + /* the data structures in data_array contain the IDs of the resources to get the values of */ + i = 0; + do { + DEBUG("[security:read] read: %d\n", (*data_array)[i].id); + result = _get_value(&(*data_array)[i], instance); + i++; + } while (i < *num_data && COAP_205_CONTENT == result); + +out: + return result; +} + +static uint8_t _write_cb(uint16_t instance_id, int num_data, lwm2m_data_t *data_array, + lwm2m_object_t *object) +{ + lwm2m_obj_security_inst_t *instance; + int64_t value; + uint8_t result = COAP_404_NOT_FOUND; + int i = 0; + + DEBUG("[lwm2m:security:write]: looking for instance %d\n", instance_id); + + /* try to get the requested instance from the object list */ + instance = (lwm2m_obj_security_inst_t *)lwm2m_list_find(object->instanceList, instance_id); + if (!instance) { + DEBUG("[lwm2m:security:write]: not found\n"); + goto out; + } + + /* iterate over the array of data to write */ + do { + lwm2m_data_t *data = &data_array[i]; + + switch (data->id) { + case LWM2M_SECURITY_URI_ID: + DEBUG("[lwm2m:security:write]: writing URI\n"); + if (data->value.asBuffer.length > CONFIG_LWM2M_URI_MAX_SIZE - 1) { + result = COAP_400_BAD_REQUEST; + } + + strncpy(instance->uri, (char *)data->value.asBuffer.buffer, + data->value.asBuffer.length); + instance->uri[data->value.asBuffer.length] = '\0'; + result = COAP_204_CHANGED; + break; + + case LWM2M_SECURITY_BOOTSTRAP_ID: + DEBUG("[lwm2m:security:write]: writing bootstrap\n"); + if (lwm2m_data_decode_bool(data, &(instance->is_bootstrap)) == 1) { + result = COAP_204_CHANGED; + } + else { + result = COAP_400_BAD_REQUEST; + } + break; + + case LWM2M_SECURITY_SECURITY_ID: + DEBUG("[lwm2m:security:write]: writing sec. mode\n"); + if (lwm2m_data_decode_int(data, &value) == 1) { + /* check if it is a valid security mode */ + if (LWM2M_SECURITY_MODE_NONE == value || + LWM2M_SECURITY_MODE_PRE_SHARED_KEY == value || + LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY == value || + LWM2M_SECURITY_MODE_CERTIFICATE == value) { + instance->security_mode = value; + result = COAP_204_CHANGED; + break; + } + } + result = COAP_400_BAD_REQUEST; + break; + + case LWM2M_SECURITY_SHORT_SERVER_ID: + DEBUG("[lwm2m:security:write]: writing short ID\n"); + if (lwm2m_data_decode_int(data, &value) == 1) { + /* check valid range of value */ + if (value > 0 && value < UINT16_MAX) { + instance->short_id = value; + result = COAP_204_CHANGED; + break; + } + } + result = COAP_400_BAD_REQUEST; + break; + + case LWM2M_SECURITY_HOLD_OFF_ID: + DEBUG("[lwm2m:security:write]: writing hold off time\n"); + if (lwm2m_data_decode_int(data, &value) == 1) { + /* check valid range of value */ + if (value >= 0 && value <= UINT32_MAX) { + instance->client_hold_off_time = value; + result = COAP_204_CHANGED; + break; + } + } + result = COAP_400_BAD_REQUEST; + break; + + case LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID: + DEBUG("[lwm2m:security:write]: writing bootstrap timeout\n"); + if (lwm2m_data_decode_int(data, &value) == 1) { + /* check valid range of value */ + if (value >= 0 && value <= UINT32_MAX) { + instance->bs_account_timeout = value; + result = COAP_204_CHANGED; + break; + } + } + result = COAP_400_BAD_REQUEST; + break; + + case LWM2M_SECURITY_PUBLIC_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + DEBUG("[lwm2m:security:write]: writing pub. key or ID\n"); + if (data->value.asBuffer.length > + CONFIG_LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE) { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + + /* copy the new value */ + memcpy(instance->pub_key_or_id, data->value.asBuffer.buffer, + data->value.asBuffer.length); + + /* if the size changed we need to modify the registered credential */ + if (instance->pub_key_or_id_len != data->value.asBuffer.length) { + instance->pub_key_or_id_len = data->value.asBuffer.length; + _update_credential(instance); + } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + result = COAP_204_CHANGED; + break; + + case LWM2M_SECURITY_SECRET_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + DEBUG("[lwm2m:security:write]: writing sec. key\n"); + if (data->value.asBuffer.length > + CONFIG_LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE) { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + + /* copy the new value */ + memcpy(instance->secret_key, data->value.asBuffer.buffer, + data->value.asBuffer.length); + + /* if the size changed we need to modify the registered credential */ + if (instance->secret_key_len != data->value.asBuffer.length) { + instance->secret_key_len = data->value.asBuffer.length; + _update_credential(instance); + } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + result = COAP_204_CHANGED; + break; + + case LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID: +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + DEBUG("[lwm2m:security:write]: writing server pub. key\n"); + if (data->value.asBuffer.length > + CONFIG_LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE) { + result = COAP_500_INTERNAL_SERVER_ERROR; + } + + /* copy the new value */ + memcpy(instance->server_pub_key, data->value.asBuffer.buffer, + data->value.asBuffer.length); + + /* if the size changed we need to modify the registered credential */ + if (instance->server_pub_key_len != data->value.asBuffer.length) { + instance->server_pub_key_len = data->value.asBuffer.length; + _update_credential(instance); + } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + result = COAP_204_CHANGED; + break; + + /* not implemented, ignore for now */ + case LWM2M_SECURITY_SMS_SECURITY_ID: + case LWM2M_SECURITY_SMS_KEY_PARAM_ID: + case LWM2M_SECURITY_SMS_SECRET_KEY_ID: + case LWM2M_SECURITY_SMS_SERVER_NUMBER_ID: + result = COAP_204_CHANGED; + break; + + default: + DEBUG("[lwm2m:security:write]: unknown resource %d\n", data->id); + result = COAP_404_NOT_FOUND; + } + i++; + } while (i < num_data && result == COAP_204_CHANGED); + + DEBUG("[lwm2m:security:write]: done\n"); + +out: + return result; +} + +static uint8_t _delete_cb(uint16_t instance_id, lwm2m_object_t *object) +{ + uint8_t result = COAP_404_NOT_FOUND; + lwm2m_obj_security_inst_t *instance; + + mutex_lock(&_security_object.lock); + DEBUG("[lwm2m:security:write]: looking for instance %d\n", instance_id); + + /* try to remove the requested instance from the list */ + object->instanceList = LWM2M_LIST_RM(object->instanceList, instance_id, &instance); + + /* check if the instance was found */ + if (NULL == instance) { + result = COAP_404_NOT_FOUND; + goto free_out; + } + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + /* if there is an associated credential, de-register */ + if (instance->cred_tag != CREDMAN_TAG_EMPTY) { + credman_type_t type = instance->security_mode == LWM2M_SECURITY_MODE_PRE_SHARED_KEY ? + CREDMAN_TYPE_PSK : CREDMAN_TYPE_ECDSA; + credman_delete(instance->cred_tag, type); + lwm2m_client_remove_credential(instance->cred_tag); + } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + + /* add instance to free instances list */ + instance->list.id = UINT16_MAX; + _FREE_INSTANCES(_security_object) = (lwm2m_obj_security_inst_t *) LWM2M_LIST_ADD( + _FREE_INSTANCES(_security_object), instance); + result = COAP_202_DELETED; + +free_out: + mutex_unlock(&_security_object.lock); + return result; +} + +static uint8_t _create_cb(uint16_t instance_id, int num_data, lwm2m_data_t *data_array, + lwm2m_object_t *object) +{ + lwm2m_obj_security_inst_t *instance; + uint8_t result; + + mutex_lock(&_security_object.lock); + + /* check that the ID is free to use */ + if (LWM2M_LIST_FIND(_USED_INSTANCES(_security_object), instance_id ) != NULL) { + DEBUG("[lwm2m:security]: instance ID %" PRIu16 " already in use\n", instance_id); + result = COAP_400_BAD_REQUEST; + goto free_out; + } + + /* try to allocate an instance, by popping a free node from the list */ + _FREE_INSTANCES(_security_object) = (lwm2m_obj_security_inst_t *) lwm2m_list_remove( + (lwm2m_list_t *) _FREE_INSTANCES(_security_object), + UINT16_MAX, + (lwm2m_list_t **) &instance + ); + + if (!instance) { + DEBUG("[lwm2m:security:create] can't allocate free instance\n"); + result = COAP_500_INTERNAL_SERVER_ERROR; + goto free_out; + } + + memset(instance, 0, sizeof(lwm2m_obj_security_inst_t)); + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + instance->cred_tag = CREDMAN_TAG_EMPTY; +#endif + + /* add to the object instance list */ + instance->list.id = instance_id; + object->instanceList = LWM2M_LIST_ADD(object->instanceList, instance); + + /* write incoming data to the instance */ + result = _write_cb(instance_id, num_data, data_array, object); + + if (result != COAP_204_CHANGED) { + _delete_cb(instance_id, object); + } + else { + result = COAP_201_CREATED; + goto free_out; + } + +free_out: + mutex_unlock(&_security_object.lock); + return result; +} + +lwm2m_object_t *lwm2m_object_security_init(lwm2m_client_data_t *client_data) +{ + /* initialize the instances */ + for (unsigned i = 0; i < CONFIG_LWM2M_OBJ_SECURITY_INSTANCES_MAX; i++) { + _security_object.instances[i].list.next = NULL; + _security_object.instances[i].list.id = UINT16_MAX; + + _FREE_INSTANCES(_security_object) = (lwm2m_obj_security_inst_t *) LWM2M_LIST_ADD( + _FREE_INSTANCES(_security_object), &(_security_object.instances[i])); + } + + _security_object.wakaama_object.userData = client_data; + return &(_security_object.wakaama_object); +} + +static int _initialize_new_instance(lwm2m_obj_security_inst_t *instance, uint16_t instance_id, + const lwm2m_obj_security_args_t *args) +{ + memset(instance, 0, sizeof(lwm2m_obj_security_inst_t)); + + instance->list.id = instance_id; + instance->short_id = args->server_id; + instance->security_mode = args->security_mode; + instance->is_bootstrap = args->is_bootstrap; + instance->client_hold_off_time = args->client_hold_off_time; + instance->bs_account_timeout = args->bootstrap_account_timeout; + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + instance->cred_tag = CREDMAN_TAG_EMPTY; +#endif + + /* copy the URI locally */ + size_t uri_len = strlen(args->server_uri); + if (uri_len > CONFIG_LWM2M_URI_MAX_SIZE - 1) { + DEBUG("[lwm2m:security]: can't copy URI, not enough space\n"); + return -ENOMEM; + } + + strncpy(instance->uri, args->server_uri, uri_len); + instance->uri[uri_len] = '\0'; + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + if (LWM2M_SECURITY_MODE_NONE != args->security_mode) { + if (LWM2M_SECURITY_MODE_CERTIFICATE == args->security_mode) { + DEBUG("[lwm2m:security]: certificate mode not supported\n"); + return -EINVAL; + } + + /* copy keys locally */ + if (LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY == args->security_mode) { + if (!args->server_pub_key || + args->server_pub_key_len > CONFIG_LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE) { + DEBUG("[lwm2m:security]: Invalid server public key\n"); + return -EINVAL; + } + memcpy(instance->server_pub_key, args->server_pub_key, args->server_pub_key_len); + instance->server_pub_key_len = args->server_pub_key_len; + } + + if (!args->pub_key_or_id || + args->pub_key_or_id_len > CONFIG_LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE) { + DEBUG("[lwm2m:security]: Invalid public key or ID\n"); + return -EINVAL; + } + memcpy(instance->pub_key_or_id, args->pub_key_or_id, args->pub_key_or_id_len); + instance->pub_key_or_id_len = args->pub_key_or_id_len; + + if (!args->secret_key || + args->secret_key_len > CONFIG_LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE) { + DEBUG("[lwm2m:security]: Invalid secret key\n"); + return -EINVAL; + } + memcpy(instance->secret_key, args->secret_key, args->secret_key_len); + instance->secret_key_len = args->secret_key_len; + + /* assign a credential tag */ + instance->cred_tag = ++_security_object.tag_count; + + if (_update_credential(instance) < 0) { + DEBUG("[lwm2m:security]: could not register the credential\n"); + return -ENOMEM; + } + } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + + return 0; +} + +int lwm2m_object_security_instance_create(const lwm2m_obj_security_args_t *args, + int32_t instance_id) +{ + assert(args); + int result = -ENOMEM; + uint16_t _instance_id; + lwm2m_obj_security_inst_t *instance = NULL; + + /* lock object */ + mutex_lock(&_security_object.lock); + + /* some sanity checks */ + if (!args || !args->server_id || !args->server_uri) { + DEBUG("[lwm2m:security]: invalid arguments\n"); + result = -EINVAL; + goto free_out; + } + + DEBUG("[lwm2m:security]: creating new instance\n"); + + /* determine ID for new instance */ + if (instance_id < 0) { + _instance_id = lwm2m_list_newId((lwm2m_list_t *)_USED_INSTANCES(_security_object)); + } + else { + /* sanity check */ + if (instance_id >= (UINT16_MAX - 1)) { + DEBUG("[lwm2m:security]: instance ID %" PRIi32 " is too big\n", instance_id); + result = -EINVAL; + goto free_out; + } + + _instance_id = (uint16_t)instance_id; + + /* check that the ID is free to use */ + if (LWM2M_LIST_FIND(_USED_INSTANCES(_security_object), _instance_id ) != NULL) { + DEBUG("[lwm2m:security]: instance ID %" PRIi32 " already in use\n", instance_id); + result = -EINVAL; + goto free_out; + } + } + + /* try to allocate an instance, by popping a free node from the list */ + _FREE_INSTANCES(_security_object) = (lwm2m_obj_security_inst_t *) lwm2m_list_remove( + (lwm2m_list_t *) _FREE_INSTANCES(_security_object), + UINT16_MAX, + (lwm2m_list_t **) &instance + ); + + if (!instance) { + DEBUG("[lwm2m:security]: can't allocate new instance\n"); + goto free_out; + } + + result = _initialize_new_instance(instance, _instance_id, args); + if (result < 0) { + DEBUG("[lwm2m:security]: could not initialize new instance\n"); + goto free_out; + } + + DEBUG("[lwm2m:security]: added instance with URI: %s\n", instance->uri); + + /* add the new instance to the list */ + _USED_INSTANCES(_security_object) = LWM2M_LIST_ADD(_USED_INSTANCES(_security_object), instance); + result = instance->list.id; + +free_out: + mutex_unlock(&_security_object.lock); + return result; +} + +credman_tag_t lwm2m_object_security_get_credential(uint16_t instance_id) +{ +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + lwm2m_obj_security_inst_t *instance; + + /* try to get the requested instance from the object list */ + instance = (lwm2m_obj_security_inst_t *)lwm2m_list_find(_USED_INSTANCES(_security_object), + instance_id); + if (NULL == instance) { + DEBUG("[lwm2m:security]: no instance %d\n", instance_id); + return CREDMAN_TAG_EMPTY; + } + + return instance->cred_tag; +#else + (void) instance_id; + return CREDMAN_TAG_EMPTY; +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ +} diff --git a/pkg/wakaama/doc.txt b/pkg/wakaama/doc.txt index 53dc76704c01..800a07a68a4c 100644 --- a/pkg/wakaama/doc.txt +++ b/pkg/wakaama/doc.txt @@ -1,7 +1,9 @@ /** - * @defgroup pkg_wakaama Wakaama LwM2M implementation + * @defgroup pkg_wakaama LwM2M - Lightweight Machine to Machine * @ingroup pkg * @ingroup net - * @brief Provides the Wakaama implementation of LwM2M + * @brief LwM2M implementation based on the Wakaama package * @see https://github.com/eclipse/wakaama - */ \ No newline at end of file + * + * For a list of the supported objects see @ref lwm2m_objects. + */ diff --git a/pkg/wakaama/include/lwm2m_client_config.h b/pkg/wakaama/include/lwm2m_client_config.h index 6e92ac040edf..266f58be2f90 100644 --- a/pkg/wakaama/include/lwm2m_client_config.h +++ b/pkg/wakaama/include/lwm2m_client_config.h @@ -72,23 +72,6 @@ extern "C" { #define CONFIG_LWM2M_DEVICE_TTL 300 #endif -/** - * @brief LwM2M server URI to register/bootstrap with - * - * @note The host part of the URI MUST be a valid IPv6 address. Host names can - * not be resolved at this time. - */ -#ifndef CONFIG_LWM2M_SERVER_URI -#define CONFIG_LWM2M_SERVER_URI "coap://[fd00:dead:beef::1]" -#endif - -/** - * @brief Numeric ID of CONFIG_LWM2M_SERVER_URI - */ -#ifndef CONFIG_LWM2M_SERVER_ID -#define CONFIG_LWM2M_SERVER_ID 10 -#endif - /** * @brief Alternate path to place LwM2M resources */ @@ -97,7 +80,7 @@ extern "C" { #endif /** - * @brief Define to 1 to specify that @ref CONFIG_LWM2M_SERVER_URI is a bootstrap server + * @brief Define to 1 to add bootstrap server support * * To define just add it to your `CFLAGS` in your application's Makefile: * @@ -234,6 +217,20 @@ extern "C" { #define CONFIG_LWM2M_DEVICE_BINDINGS "U" #endif +/** + * @brief Number to use as base for assigning tags to @ref net_credman credentials. + */ +#ifndef CONFIG_LWM2M_CREDMAN_TAG_BASE +#define CONFIG_LWM2M_CREDMAN_TAG_BASE (10U) +#endif + +/** + * @brief Maximum length of an URI allowed. + */ +#ifndef CONFIG_LWM2M_URI_MAX_SIZE +#define CONFIG_LWM2M_URI_MAX_SIZE 64 +#endif + #ifdef __cplusplus } #endif diff --git a/pkg/wakaama/include/lwm2m_client_objects.h b/pkg/wakaama/include/lwm2m_client_objects.h index 0cd28d03d41f..174eb445561a 100644 --- a/pkg/wakaama/include/lwm2m_client_objects.h +++ b/pkg/wakaama/include/lwm2m_client_objects.h @@ -40,18 +40,6 @@ extern "C" { #define LWM2M_ACC_CTRL_CREATE (1 << 4) /**< Creation access */ /** @} */ -/** - * @brief Creates a LwM2M security object with the default configuration from - * net/lwm2m.h - * - * @param[in, out] client_data Pointer to a LwM2M client data descriptor - * - * @return Pointer to the created object in success - * @return NULL otherwise - */ -lwm2m_object_t *lwm2m_client_get_security_object( - lwm2m_client_data_t *client_data); - /** * @brief Creates a LwM2M server object with the default configuration from * net/lwm2m.h diff --git a/pkg/wakaama/include/objects/security.h b/pkg/wakaama/include/objects/security.h new file mode 100644 index 000000000000..87fa0b15a555 --- /dev/null +++ b/pkg/wakaama/include/objects/security.h @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2021 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup lwm2m_objects + * @defgroup lwm2m_objects_security Security LwM2M object + * @brief Security object implementation for LwM2M client using Wakaama + * + * @experimental This API is considered experimental and may change in future releases without + * deprecation process. + * + * This implements the LwM2M Security object as specified in the Appendix E1 of + * the LwM2M specification. + * + * So far only NO_SEC, PSK (Pre-shared key) and RPK (Raw public key) modes are available. + * + * ## Resources + * + * For an XML description of the object see + * https://raw.githubusercontent.com/OpenMobileAlliance/lwm2m-registry/prod/version_history/0-1_0.xml. + * + * | Name | ID | Mandatory | Type | Range | Units | Implemented | + * | ----------------------- | -- | --------- | ------- | ------- | ----- | ---------- | + * | Server URI | 0 | Yes | String | | | Yes | + * | Bootstrap Server | 1 | Yes | Boolean | | | Yes | + * | Security Mode | 2 | Yes | Integer | 0-3 | | Yes | + * | Public Key or ID | 3 | Yes | Opaque | | | Yes | + * | Server Public Key | 4 | Yes | Opaque | | | Yes | + * | Secret Key | 5 | Yes | Opaque | | | Yes | + * | SMS Security Mode | 6 | No | Integer | 0-255 | | No | + * | SMS Binding Key Param. | 7 | No | Opaque | 6 B | | No | + * | SMS Binding Secret Keys | 8 | No | Opaque | 32-48 B | | No | + * | Server SMS Number | 9 | No | String | | | No | + * | Short Server ID | 10 | No | Integer | 1-65535 | | Yes | + * | Client Hold Off Time | 11 | No | Integer | | s | Yes | + * | BS Account Timeout | 12 | No | Integer | | s | Yes | + * + * ## Usage + * + * ### Pre-shared keys + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * // assuming buffers psk_id and psk_key containing credential information + * // assuming client_data is a valid lwm2m_client_data_t instance + * // [...] + * + * // prepare instance arguments + * lwm2m_obj_security_args_t args = { + * .server_id = CONFIG_LWM2M_SERVER_SHORT_ID, + * .server_uri = CONFIG_LWM2M_SERVER_URI, + * .security_mode = LWM2M_SECURITY_MODE_PRE_SHARED_KEY, + * .pub_key_or_id = psk_id, + * .pub_key_or_id_len = sizeof(psk_id) - 1, + * .secret_key = psk_key, + * .secret_key_len = sizeof(psk_key) - 1, + * .is_bootstrap = false, + * .client_hold_off_time = 5, + * .bootstrap_account_timeout = 0 + * }; + * + * // initialize the security object and get handle + * lwm2m_object_t *sec_obj = lwm2m_object_security_init(&client_data); + * + * // instantiate a new security object with instance ID 1 + * int res = lwm2m_object_security_instance_create(&args, 1); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * ### Raw public keys + * + * To use this security mode the following keys are required: + * - server public key (`SubjectPublicKeyInfo` DER encoded, according to RFC5280) + * - own public (`SubjectPublicKeyInfo` DER encoded) and private (as a `ECPrivateKey` DER encoded + * sequence, according to RFC5915)keys. See bellow on how they can be generated. + * + * It is possible that you may need to increase @ref CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP when using + * RPK mode. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * // assuming buffers rpk_pub, rpk_priv, and server_rpk_pub containing the elliptic curve keys + * // assuming client_data is a valid lwm2m_client_data_t instance + * // [...] + * + * // prepare instance arguments + * lwm2m_obj_security_args_t args = { + * .server_id = CONFIG_LWM2M_SERVER_SHORT_ID, + * .server_uri = CONFIG_LWM2M_SERVER_URI, + * .security_mode = LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY, + * .pub_key_or_id = rpk_pub, + * .pub_key_or_id_len = sizeof(rpk_pub), + * .secret_key = rpk_priv, + * .secret_key_len = sizeof(rpk_priv), + * .server_pub_key = server_rpk_pub, + * .server_pub_key_len = sizeof(server_rpk_pub), + * .is_bootstrap = false, + * .client_hold_off_time = 5, + * .bootstrap_account_timeout = 0 + * }; + * + * // initialize the security object and get handle + * lwm2m_object_t *sec_obj = lwm2m_object_security_init(&client_data); + * + * // instantiate a new security object with next available instance ID + * int id = lwm2m_object_security_instance_create(&args, -1); + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * #### Generating the keys + * + * The local key pair can be generated using [OpenSSL](https://www.openssl.org/). + * 1. Generate the key pair using the secp256r1 parameters: + * ~~~~~~~~~~~~~~~~~~~~ + * $ openssl ecparam -name secp256r1 -genkey -outform der -out keys.der + * ~~~~~~~~~~~~~~~~~~~~ + * + * 2. Get the public part of the key: + * ~~~~~~~~~~~~~~~~~~~~ + * $ openssl ec -in keys.der -inform DER -outform DER -pubout | xxd -i + * ~~~~~~~~~~~~~~~~~~~~ + * + * 3. Get the private part of the key: + * ~~~~~~~~~~~~~~~~~~~~ + * $ openssl ec -in keys.der -inform DER -no_public -outform DER | xxd -i + * ~~~~~~~~~~~~~~~~~~~~ + * + * 4. If your server requires your public key as X-Y coordinates you can dump it as text, remove the + * first byte and split the rest in two equally-sized parts. The first part will be X, the + * second Y. + * ~~~~~~~~~~~~~~~~~~~~ + * $ openssl ec -in keys.der -inform DER -text + * + * [...] + * pub: + * 04:a0:c3:8e:cb:a1:02:eb:5d:25:96:98:bb:60:8e: + * 28:19:56:06:96:70:15:9b:54:ff:d9:60:32:c3:3e: + * 89:08:ae:3a:33:2f:54:5f:68:a2:ac:d1:b9:df:2b: + * 79:65:49:3f:1c:ae:64:7a:32:02:e4:32:8d:6b:22: + * 67:83:0d:7c:b2 + * ASN1 OID: prime256v1 + * NIST CURVE: P-256 + * [...] + * ~~~~~~~~~~~~~~~~~~~~ + * + * Following the example above we have: + * ~~~~~~~~~~~~~~~~~~~~ + * X : a0c38ecba102eb5d259698bb608e281956069670159b54ffd96032c33e8908ae + * Y : 3a332f545f68a2acd1b9df2b7965493f1cae647a3202e4328d6b2267830d7cb2 + * ~~~~~~~~~~~~~~~~~~~~ + * @{ + * + * @file + * + * @author Leandro Lanzieri + */ + +#ifndef OBJECTS_SECURITY_H +#define OBJECTS_SECURITY_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include +#include + +#include "liblwm2m.h" +#include "net/credman.h" +#include "lwm2m_client.h" +#include "lwm2m_client_config.h" + +/* these are defined in liblwm2m.h, and are reproduced here for documentation purposes */ +#ifdef DOXYGEN +/** + * @name LwM2M Security object security modes + * @{ + */ +/** + * @brief Pre-Shared keys mode + */ +#define LWM2M_SECURITY_MODE_PRE_SHARED_KEY 0 + +/** + * @brief Raw public keys mode + */ +#define LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY 1 + +/** + * @brief Certificate mode + */ +#define LWM2M_SECURITY_MODE_CERTIFICATE 2 + +/** + * @brief No security mode + */ +#define LWM2M_SECURITY_MODE_NONE 3 +/** @} */ + +/** + * @name Resource IDs for the LWM2M Security Object + * @{ + */ +/** + * @brief Server URI + */ +#define LWM2M_SECURITY_URI_ID 0 + +/** + * @brief Bootstrap server + */ +#define LWM2M_SECURITY_BOOTSTRAP_ID 1 + +/** + * @brief Security mode + */ +#define LWM2M_SECURITY_SECURITY_ID 2 + +/** + * @brief Public key or ID + */ +#define LWM2M_SECURITY_PUBLIC_KEY_ID 3 + +/** + * @brief Server public key + */ +#define LWM2M_SECURITY_SERVER_PUBLIC_KEY_ID 4 + +/** + * @brief Secret key + */ +#define LWM2M_SECURITY_SECRET_KEY_ID 5 + +/** + * @brief SMS security mode + */ +#define LWM2M_SECURITY_SMS_SECURITY_ID 6 + +/** + * @brief SMS binding key parameters + */ +#define LWM2M_SECURITY_SMS_KEY_PARAM_ID 7 + +/** + * @brief SMS binding secret keys + */ +#define LWM2M_SECURITY_SMS_SECRET_KEY_ID 8 + +/** + * @brief Server SMS number + */ +#define LWM2M_SECURITY_SMS_SERVER_NUMBER_ID 9 + +/** + * @brief Short server ID + */ +#define LWM2M_SECURITY_SHORT_SERVER_ID 10 + +/** + * @brief Client hold-off time + */ +#define LWM2M_SECURITY_HOLD_OFF_ID 11 + +/** + * @brief Boostrap server account timeout + */ +#define LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID 12 +/** @} */ +#endif /* DOXYGEN */ + +/** + * @defgroup lwm2m_objects_security_config LwM2M Security object compile configurations + * @ingroup lwm2m_client_config + * @{ + */ +/** + * @brief Maximum number of instances of the Security object + */ +#ifndef CONFIG_LWM2M_OBJ_SECURITY_INSTANCES_MAX +#define CONFIG_LWM2M_OBJ_SECURITY_INSTANCES_MAX (2) +#endif + +/** + * @brief Buffer size of the public key or ID resource + */ +#ifndef CONFIG_LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE +#define CONFIG_LWM2M_OBJ_SECURITY_PUB_KEY_ID_BUFSIZE (128) +#endif + +/** + * @brief Buffer size of the server public key resource + */ +#ifndef CONFIG_LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE +#define CONFIG_LWM2M_OBJ_SECURITY_SERVER_PUB_KEY_BUFSIZE (128) +#endif + +/** + * @brief Buffer size of the secret key resource + */ +#ifndef CONFIG_LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE +#define CONFIG_LWM2M_OBJ_SECURITY_SEC_KEY_BUFSIZE (64) +#endif +/** @} */ + +/** + * @brief Arguments for a new Security object instance creation + * (@ref lwm2m_object_security_instance_create). + */ +typedef struct lwm2m_obj_security_args { + /** + * @brief Server's short ID the instance is associated to. + */ + uint16_t server_id; + + /** + * @brief Server's URI the instance is associated to. + * + * @note This buffer will be copied internally. + */ + const char *server_uri; + + /** + * @brief Security mode to use. For now only @ref LWM2M_SECURITY_MODE_NONE and + * @ref LWM2M_SECURITY_MODE_PRE_SHARED_KEY and + * @ref LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY are supported. + */ + uint8_t security_mode; + + /** + * @brief Pointer to the Key ID when using @ref LWM2M_SECURITY_MODE_PRE_SHARED_KEY. + * Pointer to the public key encoded as a `SubjectPublicKeyInfo` sequence when using + * @ref LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY (see @ref credman_load_public_key). + * May be `NULL` when @ref LWM2M_SECURITY_MODE_NONE is used. + * + * @note This buffer will be copied internally. + */ + const uint8_t *pub_key_or_id; + + /** + * @brief Length of @ref lwm2m_obj_security_args_t::pub_key_or_id. + */ + size_t pub_key_or_id_len; + + /** + * @brief Pointer to the Key when using @ref LWM2M_SECURITY_MODE_PRE_SHARED_KEY. + * Pointer to the private key PKCS8 DER encoded when using + * @ref LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY (see @ref credman_load_private_key). + * May be `NULL` when @ref LWM2M_SECURITY_MODE_NONE is used. + * + * @note This buffer will be copied internally. + */ + const uint8_t *secret_key; + + /** + * @brief Length of @ref lwm2m_obj_security_args_t::secret_key. + */ + size_t secret_key_len; + + /** + * @brief Pointer to the server public key encoded as a `SubjectPublicKeyInfo` sequence when + * using @ref LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY (see @ref credman_load_public_key). + * May be `NULL` when @ref LWM2M_SECURITY_MODE_NONE or + * @ref LWM2M_SECURITY_MODE_PRE_SHARED_KEY are used. + * + * @note This buffer will be copied internally. + */ + const uint8_t *server_pub_key; + + /** + * @brief Length of @ref lwm2m_obj_security_args_t::server_pub_key. + */ + size_t server_pub_key_len; + + /** + * @brief When `true` the security instance is associated to the Bootstrap-Server. + */ + bool is_bootstrap; + + /** + * @brief Time, in seconds, to wait before initiating a 'Client Initiated Bootstrap', after it + * has been determined that it should be initiated. + */ + uint32_t client_hold_off_time; + + /** + * @brief Time, in seconds, that the client waits before it purges the Bootstrap-Server's + * account. 0 means never. + */ + uint32_t bootstrap_account_timeout; +} lwm2m_obj_security_args_t; + +/** + * @brief Initialize the Security object. + * + * @param[in] client_data LwM2M client data. + * + * @return Pointer to the Security object on success + */ +lwm2m_object_t *lwm2m_object_security_init(lwm2m_client_data_t *client_data); + +/** + * @brief Create a new Security instance and add it to the @p object list. + * + * @param[in] args Initialize structure with the parameter for the instance. May + * not be NULL. + * @param[in] instance_id ID for the new instance. It must be between 0 and + * (UINT16_MAX - 1), if -1 the next available ID will be used. + * + * @return Instance ID (> 0) on success + * @return -EINVAL if an invalid @p instance_id is given + * @return -ENOMEM if no memory is available to create a new instance + */ +int lwm2m_object_security_instance_create(const lwm2m_obj_security_args_t *args, + int32_t instance_id); + +/** + * @brief Get the credential of a given instance of the security object. + * + * @param[in] instance_id ID of the instance. + * + * @return Credential tag. + * @retval CREDMAN_TAG_EMPTY when no credential is assigned. + */ +credman_tag_t lwm2m_object_security_get_credential(uint16_t instance_id); + +#ifdef __cplusplus +} +#endif + +#endif /* OBJECTS_SECURITY_H */ +/** @} */ diff --git a/pkg/wakaama/wakaama_client.mk b/pkg/wakaama/wakaama_client.mk index 6cc649fc29d2..c2b7e38c3093 100644 --- a/pkg/wakaama/wakaama_client.mk +++ b/pkg/wakaama/wakaama_client.mk @@ -2,7 +2,6 @@ MODULE = wakaama_client SRC = \ object_server.c \ - object_security.c \ object_access_control.c \ # From 70c4c6b8c1e74ef519fa8d1162283bd96b959d52 Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Fri, 19 Mar 2021 11:34:33 +0100 Subject: [PATCH 2/8] pkg/wakaama/client: allow to specify server instance ID --- pkg/wakaama/contrib/lwm2m_client_objects.c | 5 ++--- pkg/wakaama/include/lwm2m_client_objects.h | 1 + 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/wakaama/contrib/lwm2m_client_objects.c b/pkg/wakaama/contrib/lwm2m_client_objects.c index e4cd489cf0ac..79a7e992051f 100644 --- a/pkg/wakaama/contrib/lwm2m_client_objects.c +++ b/pkg/wakaama/contrib/lwm2m_client_objects.c @@ -31,12 +31,11 @@ lwm2m_object_t *get_server_object(int server_id, const char *binding, int lifetime, bool storing); lwm2m_object_t *lwm2m_get_object_device(void); -lwm2m_object_t *lwm2m_client_get_server_object( - lwm2m_client_data_t *client_data) +lwm2m_object_t *lwm2m_client_get_server_object(lwm2m_client_data_t *client_data, + int server_id) { (void)client_data; lwm2m_object_t *ret; - int server_id = CONFIG_LWM2M_SERVER_ID; int lifetime = CONFIG_LWM2M_DEVICE_TTL; ret = get_server_object(server_id, CONFIG_LWM2M_DEVICE_BINDINGS, lifetime, diff --git a/pkg/wakaama/include/lwm2m_client_objects.h b/pkg/wakaama/include/lwm2m_client_objects.h index 174eb445561a..f2331b572e19 100644 --- a/pkg/wakaama/include/lwm2m_client_objects.h +++ b/pkg/wakaama/include/lwm2m_client_objects.h @@ -45,6 +45,7 @@ extern "C" { * net/lwm2m.h * * @param[in, out] client_data Pointer to a LwM2M client data descriptor + * @param[in] server_id Server ID (SID) to assign to the new instance * * @return Pointer to the created object * @return NULL otherwise From 3d012bfd2c62e48d503a974e6d22e3fa2509e342 Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Tue, 23 Mar 2021 11:50:57 +0100 Subject: [PATCH 3/8] pkg/wakaama/contrib: cleanup device object implementation --- pkg/wakaama/contrib/lwm2m_client_objects.c | 9 +- pkg/wakaama/contrib/objects/Kconfig | 3 +- pkg/wakaama/contrib/objects/Kconfig.device | 68 ++++ pkg/wakaama/contrib/objects/device.c | 352 ++++++++++----------- pkg/wakaama/include/lwm2m_client_config.h | 131 +------- pkg/wakaama/include/lwm2m_client_objects.h | 13 +- pkg/wakaama/include/objects/device.h | 176 ++++++++++- 7 files changed, 420 insertions(+), 332 deletions(-) create mode 100644 pkg/wakaama/contrib/objects/Kconfig.device diff --git a/pkg/wakaama/contrib/lwm2m_client_objects.c b/pkg/wakaama/contrib/lwm2m_client_objects.c index 79a7e992051f..9483eddaa9a8 100644 --- a/pkg/wakaama/contrib/lwm2m_client_objects.c +++ b/pkg/wakaama/contrib/lwm2m_client_objects.c @@ -21,6 +21,7 @@ #include "kernel_defines.h" #include "lwm2m_client.h" +#include "objects/device.h" #include "lwm2m_client_config.h" #include "lwm2m_client_objects.h" @@ -29,7 +30,6 @@ * 'contrib/objects') */ lwm2m_object_t *get_server_object(int server_id, const char *binding, int lifetime, bool storing); -lwm2m_object_t *lwm2m_get_object_device(void); lwm2m_object_t *lwm2m_client_get_server_object(lwm2m_client_data_t *client_data, int server_id) @@ -42,10 +42,3 @@ lwm2m_object_t *lwm2m_client_get_server_object(lwm2m_client_data_t *client_data, false); return ret; } - -lwm2m_object_t *lwm2m_client_get_device_object( - lwm2m_client_data_t *client_data) -{ - (void)client_data; - return lwm2m_get_object_device(); -} diff --git a/pkg/wakaama/contrib/objects/Kconfig b/pkg/wakaama/contrib/objects/Kconfig index 416a0c342aab..1b460cde2ecb 100644 --- a/pkg/wakaama/contrib/objects/Kconfig +++ b/pkg/wakaama/contrib/objects/Kconfig @@ -5,5 +5,6 @@ # directory for more details. # -rsource "Kconfig.light_control" +rsource "Kconfig.device" rsource "Kconfig.security" +rsource "Kconfig.light_control" diff --git a/pkg/wakaama/contrib/objects/Kconfig.device b/pkg/wakaama/contrib/objects/Kconfig.device new file mode 100644 index 000000000000..a5333acbc4cb --- /dev/null +++ b/pkg/wakaama/contrib/objects/Kconfig.device @@ -0,0 +1,68 @@ +# Copyright (c) 2021 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menu "Device" + +choice + bool "Device binding and queue mode" + default LWM2M_DEVICE_BINDING_U + +config LWM2M_DEVICE_BINDING_U + bool "UDP" + +config LWM2M_DEVICE_BINDING_UQ + bool "UDP with Queue mode" + +config LWM2M_DEVICE_BINDING_S + bool "SMS" + +config LWM2M_DEVICE_BINDING_SQ + bool "SMS with Queue mode" + +config LWM2M_DEVICE_BINDING_US + bool "UDP and SMS" + +config LWM2M_DEVICE_BINDING_UQS + bool "UDP with Queue mode and SMS" + +endchoice + +config LWM2M_DEVICE_NAME + string "Device name" + default "testRIOTDevice" + help + This is the device name used to register at the LwM2M server. + +config LWM2M_DEVICE_MANUFACTURER + string "Device manufacturer" + default "A RIOT maker" + +config LWM2M_DEVICE_MODEL + string "Device model" + default "$(BOARD)" + +config LWM2M_DEVICE_TYPE + string "Device type" + default "RIOT device" + +config LWM2M_DEVICE_SERIAL + string "Device serial number" + default "undefined" + +config LWM2M_DEVICE_FW_VERSION + string "Device firmware version" + default "" + +config LWM2M_DEVICE_HW_VERSION + string "Device hardware version" + default "$(BOARD)" + +config LWM2M_DEVICE_SW_VERSION + string "Device software version" + default "" + +endmenu # Device diff --git a/pkg/wakaama/contrib/objects/device.c b/pkg/wakaama/contrib/objects/device.c index 1ecf4c0fa438..aebc1713cbea 100644 --- a/pkg/wakaama/contrib/objects/device.c +++ b/pkg/wakaama/contrib/objects/device.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2018 Beduino Master Projekt - University of Bremen + * 2021 HAW Hamburg * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level @@ -13,6 +14,7 @@ * @brief Device object implementation for LwM2M client using Wakaama * * @author Christian Manal + * @author Leandro Lanzieri * @} */ @@ -25,43 +27,90 @@ #include "objects/device.h" #include "lwm2m_client_config.h" +#define ENABLE_DEBUG 0 +#include "debug.h" + /* Set to true if reboot requested. */ static bool reboot; -/* Lookup table for static resources of device object */ -static const char *_static_resources[] = { - [LWM2M_RES_MANUFACTURER] = CONFIG_LWM2M_DEVICE_MANUFACTURER, - [LWM2M_RES_MODEL_NO] = CONFIG_LWM2M_DEVICE_MODEL, - [LWM2M_RES_SERIAL] = CONFIG_LWM2M_DEVICE_SERIAL, - [LWM2M_RES_FW_VER] = CONFIG_LWM2M_DEVICE_FW_VERSION, - [LWM2M_RES_BINDINGS] = CONFIG_LWM2M_DEVICE_BINDINGS, - [LWM2M_RES_TYPE] = CONFIG_LWM2M_DEVICE_TYPE, - [LWM2M_RES_HW_VERSION] = CONFIG_LWM2M_DEVICE_HW_VERSION, - [LWM2M_RES_SW_VERSION] = CONFIG_LWM2M_DEVICE_SW_VERSION, - [LWM2M_DEVICE_RESOURCES] = NULL -}; +/** + * @brief 'Execute' callback for the Device object. + * + * @param[in] instance_id Instance ID. Should be 0 as a single instance exists. + * @param[in] resource_id ID of the resource to execute. + * @param[in] buffer Information needed for the execution. + * @param[in] length Length of @p buffer. + * @param[in] object Device object pointer + * + * @return COAP_204_CHANGED on success + * @return COAP_404_NOT_FOUND when wrong instance specified + * @return COAP_400_BAD_REQUEST when wrong information has been sent + * @return COAP_405_METHOD_NOT_ALLOWED when trying to execute a resource that is not supported + */ +static uint8_t _execute_cb(uint16_t instance_id, uint16_t resource_id, uint8_t *buffer, int length, + lwm2m_object_t *object); + +/** + * @brief 'Read' callback for the Device object. + * + * @param[in] instance_id Instance ID. Should be 0 as a single instance exists. + * @param[in, out] num_data Number of resources requested. 0 means all. + * @param[in, out] data_array Initialized data array to output the values, + * when @p num_data != 0. Uninitialized otherwise. + * @param[in] object Device object pointer + * + * @return COAP_205_CONTENT on success + * @return COAP_404_NOT_FOUND when resource can't be found + * @return COAP_500_INTERNAL_SERVER_ERROR otherwise + */ +static uint8_t _read_cb(uint16_t instance_id, int *num_data, lwm2m_data_t **data_array, + lwm2m_object_t *object); + +/** + * @brief 'Discover' callback for the Device object. + * + * @param[in] instance_id Instance ID. Should be 0 as a single instance exists. + * @param[in, out] num_data Number of resources requested. 0 means all. + * @param[in, out] data_array Initialized data array to determine if the resource exists, + * when @p num_data != 0. Uninitialized otherwise. + * @param[in] object Device object pointer + * + * @return COAP_205_CONTENT on success + * @return COAP_404_NOT_FOUND when a resource is not supported + * @return COAP_500_INTERNAL_SERVER_ERROR otherwise + */ +static uint8_t _discover_cb(uint16_t instance_id, int *num_data, lwm2m_data_t **data_array, + lwm2m_object_t *object); -/*Descriptor of a LwM2M device object instance */ typedef struct { - uint8_t *power_sources; /**< types of power sources (0-7) */ - uint16_t *power_voltage; /**< voltage of power sources in mV */ - uint16_t *power_current; /**< current of power sources in mA */ - uint8_t battery_status; /**< battery status (0-6) */ - uint32_t mem_total; /**< amount of memory on the device in kB */ - uint16_t(*ext_dev_info)[2]; /**< external devices information */ - uint8_t ext_dev_info_len; /**< amount of external devices information */ - uint8_t error_code[7]; /**< error codes */ - uint8_t error_code_used; /**< amount of error codes used */ -} dev_data_t; - -static uint8_t prv_device_discover(uint16_t instance_id, int *num_dataP, - lwm2m_data_t **data_arrayP, - lwm2m_object_t *objectP) + lwm2m_list_t list; /**< Linked list handle */ +} lwm2m_obj_device_inst_t; + +static const lwm2m_obj_device_inst_t _instance; + +/** + * @brief Implementation of the object interface for the Device Object. + */ +static lwm2m_object_t _device_object = { + .next = NULL, + .objID = LWM2M_DEVICE_OBJECT_ID, + .instanceList = (lwm2m_list_t *)&_instance, + .readFunc = _read_cb, + .executeFunc = _execute_cb, + .discoverFunc = _discover_cb, + .writeFunc = NULL, + .deleteFunc = NULL, + .createFunc = NULL, + .userData = NULL +}; + +static uint8_t _discover_cb(uint16_t instance_id, int *num_data, lwm2m_data_t **data_array, + lwm2m_object_t *object) { uint8_t result; int i; - (void)objectP; + (void)object; if (instance_id != 0) { return COAP_404_NOT_FOUND; @@ -69,7 +118,7 @@ static uint8_t prv_device_discover(uint16_t instance_id, int *num_dataP, result = COAP_205_CONTENT; - if (*num_dataP == 0) { + if (*num_data == 0) { /* This list must contain all available resources */ uint16_t res[] = { LWM2M_RES_MANUFACTURER, LWM2M_RES_MODEL_NO, LWM2M_RES_SERIAL, @@ -80,19 +129,22 @@ static uint8_t prv_device_discover(uint16_t instance_id, int *num_dataP, }; int len = ARRAY_SIZE(res); - *data_arrayP = lwm2m_data_new(len); - if (*data_arrayP == NULL) { + *data_array = lwm2m_data_new(len); + *num_data = len; + + if (*data_array == NULL) { + DEBUG("[lwm2m:device:discover] could not allocate data array\n"); return COAP_500_INTERNAL_SERVER_ERROR; } - *num_dataP = len; + for (i = 0; i < len; i++) { - (*data_arrayP)[i].id = res[i]; + (*data_array)[i].id = res[i]; } } else { /* Check if each given resource is present */ - for (i = 0; i < *num_dataP && result == COAP_205_CONTENT; i++) { - switch ((*data_arrayP)[i].id) { + for (i = 0; i < *num_data && result == COAP_205_CONTENT; i++) { + switch ((*data_array)[i].id) { case LWM2M_RES_MANUFACTURER: case LWM2M_RES_MODEL_NO: case LWM2M_RES_SERIAL: @@ -114,23 +166,22 @@ static uint8_t prv_device_discover(uint16_t instance_id, int *num_dataP, return result; } -static uint8_t prv_device_read(uint16_t instance_id, int *num_dataP, - lwm2m_data_t **data_arrayP, - lwm2m_object_t *objectP) +static uint8_t _read_cb(uint16_t instance_id, int *num_data, lwm2m_data_t **data_array, + lwm2m_object_t *object) { + (void)object; int i; - uint8_t result = COAP_404_NOT_FOUND; - dev_data_t *data = (dev_data_t *)objectP->userData; - - (void)data; + DEBUG("[lwm2m:device:read]\n"); /* Single instance object */ if (instance_id != 0) { - goto out; + return COAP_404_NOT_FOUND; } /* Full object requested */ - if (*num_dataP == 0) { + if (*num_data == 0) { + DEBUG("[lwm2m:device:read] all resources are requested\n"); + /* This list must contain all readable resources */ uint16_t resList[] = { LWM2M_RES_MANUFACTURER, LWM2M_RES_MODEL_NO, LWM2M_RES_SERIAL, @@ -138,109 +189,103 @@ static uint8_t prv_device_read(uint16_t instance_id, int *num_dataP, LWM2M_RES_BINDINGS, LWM2M_RES_TYPE, LWM2M_RES_ERROR_CODE, }; int cnt = ARRAY_SIZE(resList); - *data_arrayP = lwm2m_data_new(cnt); - if (*data_arrayP == NULL) { - result = COAP_500_INTERNAL_SERVER_ERROR; - goto out; + *data_array = lwm2m_data_new(cnt); + *num_data = cnt; + + if (*data_array == NULL) { + DEBUG("[lwm2m:device:read] could not allocate data array\n"); + return COAP_500_INTERNAL_SERVER_ERROR; } - *num_dataP = cnt; + for (i = 0; i < cnt; i++) { - (*data_arrayP)[i].id = resList[i]; + (*data_array)[i].id = resList[i]; } } - for (i = 0; i < *num_dataP; i++) { - switch ((*data_arrayP)[i].id) { - /* Exec resources */ - case LWM2M_RES_REBOOT: - case LWM2M_RES_FRESET: - case LWM2M_RES_ERROR_CODE_RESET: - result = COAP_405_METHOD_NOT_ALLOWED; - goto out; - break; - case LWM2M_RES_ERROR_CODE: - /* TODO: Here some error reporting should be implemented. */ - lwm2m_data_encode_int(LWM2M_DEVICE_ERR_NO_ERR, *data_arrayP + i); - result = COAP_205_CONTENT; - break; - /* The rest are either static or not defined resources */ - default: - if (_static_resources[(*data_arrayP)[i].id]) { - lwm2m_data_encode_string( - _static_resources[(*data_arrayP)[i].id], - *data_arrayP + i); - result = COAP_205_CONTENT; - } - else { - result = COAP_404_NOT_FOUND; - goto out; - } - } - } + for (i = 0; i < *num_data; i++) { + lwm2m_data_t *data = &(*data_array)[i]; -out: - return result; -} + DEBUG("[lwm2m:device:read] reading resource %d\n", data->id); + switch (data->id) { + /* Exec resources, can't be read */ + case LWM2M_RES_REBOOT: + case LWM2M_RES_FRESET: + case LWM2M_RES_ERROR_CODE_RESET: + return COAP_405_METHOD_NOT_ALLOWED; + break; -static uint8_t prv_device_write(uint16_t instance_id, int num_data, - lwm2m_data_t *data_array, - lwm2m_object_t *objectP) -{ - dev_data_t *data = (dev_data_t *)objectP->userData; + case LWM2M_RES_ERROR_CODE: + /* TODO: Here some error reporting should be implemented. For now returning no error */ + lwm2m_data_encode_int(LWM2M_DEVICE_ERR_NO_ERR, data); + break; - (void)data; - (void)instance_id; - (void)num_data; - (void)data_array; + case LWM2M_RES_MANUFACTURER: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_MANUFACTURER, data); + break; - if (data_array[0].id < LWM2M_DEVICE_RESOURCES) { - /* for now not writing resources */ - return COAP_405_METHOD_NOT_ALLOWED; - } - else { - return COAP_404_NOT_FOUND; + case LWM2M_RES_MODEL_NO: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_MODEL, data); + break; + + case LWM2M_RES_SERIAL: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_SERIAL, data); + break; + + case LWM2M_RES_FW_VER: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_FW_VERSION, data); + break; + + case LWM2M_RES_BINDINGS: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_BINDINGS, data); + break; + + case LWM2M_RES_TYPE: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_TYPE, data); + break; + + case LWM2M_RES_HW_VERSION: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_HW_VERSION, data); + break; + + case LWM2M_RES_SW_VERSION: + lwm2m_data_encode_string(CONFIG_LWM2M_DEVICE_SW_VERSION, data); + break; + + case LWM2M_DEVICE_RESOURCES: + lwm2m_data_encode_string(NULL, data); + break; + + /* The rest are not defined resources */ + default: + return COAP_404_NOT_FOUND; + } } + + return COAP_205_CONTENT; } -static uint8_t prv_device_execute(uint16_t instance_id, uint16_t resource_id, - uint8_t *buffer, int length, - lwm2m_object_t *objectP) +static uint8_t _execute_cb(uint16_t instance_id, uint16_t resource_id, uint8_t *buffer, int length, + lwm2m_object_t *object) { - uint8_t result; - dev_data_t *data = (dev_data_t *)objectP->userData; - - (void)data; - (void)buffer; - (void)length; - (void)objectP; + (void)object; /* single instance object */ if (instance_id != 0) { - result = COAP_404_NOT_FOUND; - goto err_out; + return COAP_404_NOT_FOUND; } if (length != 0) { - result = COAP_400_BAD_REQUEST; - goto err_out; + return COAP_400_BAD_REQUEST; } - switch (resource_id) { - case LWM2M_RES_REBOOT: - reboot = true; - result = COAP_204_CHANGED; - break; - case LWM2M_RES_ERROR_CODE_RESET: - /* TODO */ - case LWM2M_RES_FRESET: - /* TODO Callback? */ - default: - result = COAP_405_METHOD_NOT_ALLOWED; + /* for now only rebooting is available */ + if (resource_id == LWM2M_RES_REBOOT) { + reboot = true; + return COAP_204_CHANGED; } -err_out: - return result; + return COAP_405_METHOD_NOT_ALLOWED; } /* @@ -251,63 +296,10 @@ bool lwm2m_device_reboot_requested(void) return reboot; } -lwm2m_object_t *lwm2m_get_object_device(void) +lwm2m_object_t *lwm2m_object_device_init(lwm2m_client_data_t *client_data) { - lwm2m_object_t *obj; - - obj = (lwm2m_object_t *)lwm2m_malloc(sizeof(lwm2m_object_t)); - - if (obj == NULL) { - goto err_out; - } - - memset(obj, 0, sizeof(lwm2m_object_t)); - obj->instanceList = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t)); - - if (obj->instanceList == NULL) { - goto free_obj; - } - - memset(obj->instanceList, 0, sizeof(lwm2m_list_t)); - - obj->objID = LWM2M_DEVICE_OBJECT_ID; + assert(client_data); - obj->readFunc = prv_device_read; - obj->writeFunc = prv_device_write; - obj->executeFunc = prv_device_execute; - obj->discoverFunc = prv_device_discover; - - /* Don't allocate memory for stuff that isn't used at the moment */ - /* obj->userData = lwm2m_malloc(sizeof(dev_data_t)); */ - /* if (obj->userData == NULL) { */ - /* goto free_ilist; */ - /* } */ - /* */ - /* memset(obj->userData, 0, sizeof(dev_data_t)); */ - /* INT USER DATA HERE */ - - return obj; - -/* free_ilist: */ -/* lwm2m_free(obj->instanceList); */ - -free_obj: - lwm2m_free(obj); - -err_out: - return NULL; -} - -void lwm2m_free_object_device(lwm2m_object_t *obj) -{ - if (obj == NULL) { - return; - } - if (obj->userData) { - lwm2m_free(obj->userData); - } - if (obj->instanceList) { - lwm2m_free(obj->instanceList); - } - lwm2m_free(obj); + _device_object.userData = client_data; + return &_device_object; } diff --git a/pkg/wakaama/include/lwm2m_client_config.h b/pkg/wakaama/include/lwm2m_client_config.h index 266f58be2f90..b6dd1e239b2f 100644 --- a/pkg/wakaama/include/lwm2m_client_config.h +++ b/pkg/wakaama/include/lwm2m_client_config.h @@ -59,10 +59,10 @@ extern "C" { #endif /** - * @brief Device name used to register at the LwM2M server + * @brief Default port for the local LwM2M CoAPs server */ -#ifndef CONFIG_LWM2M_DEVICE_NAME -#define CONFIG_LWM2M_DEVICE_NAME "testRIOTDevice" +#ifndef CONFIG_LWM2M_LOCAL_DTLS_PORT +#define CONFIG_LWM2M_LOCAL_DTLS_PORT "5684" #endif /** @@ -92,131 +92,6 @@ extern "C" { #define CONFIG_LWM2M_BOOTSTRAP #endif -/** - * @brief Device object manufacturer string - */ -#ifndef CONFIG_LWM2M_DEVICE_MANUFACTURER -#define CONFIG_LWM2M_DEVICE_MANUFACTURER "A RIOT maker" -#endif - -/** - * @brief Device object model. - * - * @note Defaults to the board name - */ -#ifndef CONFIG_LWM2M_DEVICE_MODEL -#define CONFIG_LWM2M_DEVICE_MODEL RIOT_BOARD -#endif - -/** - * @brief Device object serial number - */ -#ifndef CONFIG_LWM2M_DEVICE_SERIAL -#define CONFIG_LWM2M_DEVICE_SERIAL "undefined" -#endif - -/** - * @brief Device object firmware version - * - * @note Defaults to the running RIOT version - */ -#ifndef CONFIG_LWM2M_DEVICE_FW_VERSION -#define CONFIG_LWM2M_DEVICE_FW_VERSION RIOT_VERSION -#endif - -/** - * @{ - * @name Device bindings and queue modes - * - * This options are meant to be set either via Kconfig or CFLAGS: - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.mk} - * CFLAGS += -DCONFIG_LWM2M_DEVICE_BINDING_UQ - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * @note Only one option should be selected. If more than one is defined the - * priority follows this order. By default - * @ref CONFIG_LWM2M_DEVICE_BINDING_U is assumed. - */ -#ifdef DOXYGEN -/** - * @brief UDP binding - */ -#define CONFIG_LWM2M_DEVICE_BINDING_U - -/** - * @brief UDP binding with Queue mode - */ -#define CONFIG_LWM2M_DEVICE_BINDING_UQ - -/** - * @brief SMS binding - */ -#define CONFIG_LWM2M_DEVICE_BINDING_S - -/** - * @brief SMS binding with Queue mode - */ -#define CONFIG_LWM2M_DEVICE_BINDING_SQ - -/** - * @brief UDP and SMS bindings - */ -#define CONFIG_LWM2M_DEVICE_BINDING_US - -/** - * @brief UDP and SMS bindings with Queue mode - */ -#define CONFIG_LWM2M_DEVICE_BINDING_UQS -#endif -/** @} */ - -/** - * @brief Device object device type - */ -#ifndef CONFIG_LWM2M_DEVICE_TYPE -#define CONFIG_LWM2M_DEVICE_TYPE "RIOT device" -#endif - -/** - * @brief Device object hardware version - * - * @note Defaults to the board name - */ -#ifndef CONFIG_LWM2M_DEVICE_HW_VERSION -#define CONFIG_LWM2M_DEVICE_HW_VERSION RIOT_BOARD -#endif - -/** - * @brief Device object software version - * - * @note Defaults to the running RIOT version - */ -#ifndef CONFIG_LWM2M_DEVICE_SW_VERSION -#define CONFIG_LWM2M_DEVICE_SW_VERSION RIOT_VERSION -#endif - -/** - * @brief Device binding and queue mode - * - * @note Select using CONFIG_LWM2M_DEVICE_BINDING_* - */ -#if defined(CONFIG_LWM2M_DEVICE_BINDING_U) -#define CONFIG_LWM2M_DEVICE_BINDINGS "U" -#elif defined(CONFIG_LWM2M_DEVICE_BINDING_UQ) -#define CONFIG_LWM2M_DEVICE_BINDINGS "UQ" -#elif defined(CONFIG_LWM2M_DEVICE_BINDING_S) -#define CONFIG_LWM2M_DEVICE_BINDINGS "S" -#elif defined(CONFIG_LWM2M_DEVICE_BINDING_SQ) -#define CONFIG_LWM2M_DEVICE_BINDINGS "SQ" -#elif defined(CONFIG_LWM2M_DEVICE_BINDING_US) -#define CONFIG_LWM2M_DEVICE_BINDINGS "US" -#elif defined(CONFIG_LWM2M_DEVICE_BINDING_UQS) -#define CONFIG_LWM2M_DEVICE_BINDINGS "UQS" -#else -#define CONFIG_LWM2M_DEVICE_BINDINGS "U" -#endif - /** * @brief Number to use as base for assigning tags to @ref net_credman credentials. */ diff --git a/pkg/wakaama/include/lwm2m_client_objects.h b/pkg/wakaama/include/lwm2m_client_objects.h index f2331b572e19..fe510b7b0f3f 100644 --- a/pkg/wakaama/include/lwm2m_client_objects.h +++ b/pkg/wakaama/include/lwm2m_client_objects.h @@ -51,18 +51,7 @@ extern "C" { * @return NULL otherwise */ lwm2m_object_t *lwm2m_client_get_server_object( - lwm2m_client_data_t *client_data); - -/** - * @brief Creates a LwM2M device object with the default configuration from - * net/lwm2m.h - * @param[in, out] client_data Pointer to a LwM2M client data descriptor - * - * @return Pointer to the created object - * @return NULL otherwise - */ -lwm2m_object_t *lwm2m_client_get_device_object( - lwm2m_client_data_t *client_data); + lwm2m_client_data_t *client_data, int server_id); /** * @brief Creates a LwM2M access control object with the default configuration diff --git a/pkg/wakaama/include/objects/device.h b/pkg/wakaama/include/objects/device.h index fd5feb26cd35..3f29f134ca53 100644 --- a/pkg/wakaama/include/objects/device.h +++ b/pkg/wakaama/include/objects/device.h @@ -10,6 +10,33 @@ * @ingroup lwm2m_objects * @defgroup lwm2m_objects_device Device LwM2M object * @brief Device object implementation for LwM2M client using Wakaama + * + * | Name | ID | Mandatory | Type | Range | Units | Implemented | + * |-----------------------------|:--:|:---------:|:-------:|:-----:|:-----:|:-----------:| + * | Manufacturer | 0 | No | String | - | - | Yes | + * | Model Number | 1 | No | String | - | - | Yes | + * | Serial Number | 2 | No | String | - | - | Yes | + * | Firmware Version | 3 | No | String | - | - | Yes | + * | Reboot | 4 | Yes | - | - | - | Yes | + * | Factory Reset | 5 | No | - | - | - | No | + * | Available Power Sources | 6 | No | Integer | 0-7 | - | No | + * | Power Source Voltage | 7 | No | Integer | - | mV | No | + * | Power Source Current | 8 | No | Integer | - | mA | No | + * | Battery Level | 9 | No | Integer | 0-100 | % | No | + * | Memory Free | 10 | No | Integer | - | KB | No | + * | Error Code | 11 | Yes | Integer | 0-8 | - | No | + * | Reset Error Code | 12 | No | - | - | - | No | + * | Current Time | 13 | No | Time | - | - | No | + * | UTC Offset | 14 | No | String | - | - | No | + * | Timezone | 15 | No | String | - | - | No | + * | Supported Binding and Modes | 16 | Yes | String | - | - | Yes | + * | Device Type | 17 | No | String | - | - | Yes | + * | Hardware Version | 18 | No | String | - | - | Yes | + * | Software Version | 19 | No | String | - | - | Yes | + * | Battery Status | 20 | No | Integer | 0-6 | - | No | + * | Memory Total | 21 | No | Integer | - | - | No | + * | ExtDevInfo | 22 | No | Objlnk | - | - | No | + * * @{ * * @file @@ -29,6 +56,7 @@ extern "C" { #include #include "liblwm2m.h" +#include "lwm2m_client.h" #include "lwm2m_client_config.h" /** @@ -81,11 +109,153 @@ enum lwm2m_device_error_codes { }; /** - * @brief Frees the memory of @p obj device object + * @defgroup lwm2m_objects_device_config LwM2M Device Object configuration + * @ingroup lwm2m_client_config + * + * @brief Configuration options for the LwM2M Device Object. + * @{ + */ +/** + * @brief Device name used to register at the LwM2M server + */ +#ifndef CONFIG_LWM2M_DEVICE_NAME +#define CONFIG_LWM2M_DEVICE_NAME "testRIOTDevice" +#endif + +/** + * @brief Device object manufacturer string + */ +#ifndef CONFIG_LWM2M_DEVICE_MANUFACTURER +#define CONFIG_LWM2M_DEVICE_MANUFACTURER "A RIOT maker" +#endif + +/** + * @brief Device object model. + * + * @note Defaults to the board name + */ +#ifndef CONFIG_LWM2M_DEVICE_MODEL +#define CONFIG_LWM2M_DEVICE_MODEL RIOT_BOARD +#endif + +/** + * @brief Device object serial number + */ +#ifndef CONFIG_LWM2M_DEVICE_SERIAL +#define CONFIG_LWM2M_DEVICE_SERIAL "undefined" +#endif + +/** + * @brief Device object firmware version + * + * @note Defaults to the running RIOT version + */ +#ifndef CONFIG_LWM2M_DEVICE_FW_VERSION +#define CONFIG_LWM2M_DEVICE_FW_VERSION RIOT_VERSION +#endif + +/** + * @{ + * @name Device bindings and queue modes + * + * This options are meant to be set either via Kconfig or CFLAGS: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.mk} + * CFLAGS += -DCONFIG_LWM2M_DEVICE_BINDING_UQ + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * @note Only one option should be selected. If more than one is defined the + * priority follows this order. By default + * @ref CONFIG_LWM2M_DEVICE_BINDING_U is assumed. + */ +#ifdef DOXYGEN +/** + * @brief UDP binding + */ +#define CONFIG_LWM2M_DEVICE_BINDING_U + +/** + * @brief UDP binding with Queue mode + */ +#define CONFIG_LWM2M_DEVICE_BINDING_UQ + +/** + * @brief SMS binding + */ +#define CONFIG_LWM2M_DEVICE_BINDING_S + +/** + * @brief SMS binding with Queue mode + */ +#define CONFIG_LWM2M_DEVICE_BINDING_SQ + +/** + * @brief UDP and SMS bindings + */ +#define CONFIG_LWM2M_DEVICE_BINDING_US + +/** + * @brief UDP and SMS bindings with Queue mode + */ +#define CONFIG_LWM2M_DEVICE_BINDING_UQS +#endif +/** @} */ + +/** + * @brief Device object device type + */ +#ifndef CONFIG_LWM2M_DEVICE_TYPE +#define CONFIG_LWM2M_DEVICE_TYPE "RIOT device" +#endif + +/** + * @brief Device object hardware version + * + * @note Defaults to the board name + */ +#ifndef CONFIG_LWM2M_DEVICE_HW_VERSION +#define CONFIG_LWM2M_DEVICE_HW_VERSION RIOT_BOARD +#endif + +/** + * @brief Device object software version + * + * @note Defaults to the running RIOT version + */ +#ifndef CONFIG_LWM2M_DEVICE_SW_VERSION +#define CONFIG_LWM2M_DEVICE_SW_VERSION RIOT_VERSION +#endif +/** @} */ + +/** + * @brief Device binding and queue mode + * + * @note Select using CONFIG_LWM2M_DEVICE_BINDING_* + */ +#if defined(CONFIG_LWM2M_DEVICE_BINDING_U) +#define CONFIG_LWM2M_DEVICE_BINDINGS "U" +#elif defined(CONFIG_LWM2M_DEVICE_BINDING_UQ) +#define CONFIG_LWM2M_DEVICE_BINDINGS "UQ" +#elif defined(CONFIG_LWM2M_DEVICE_BINDING_S) +#define CONFIG_LWM2M_DEVICE_BINDINGS "S" +#elif defined(CONFIG_LWM2M_DEVICE_BINDING_SQ) +#define CONFIG_LWM2M_DEVICE_BINDINGS "SQ" +#elif defined(CONFIG_LWM2M_DEVICE_BINDING_US) +#define CONFIG_LWM2M_DEVICE_BINDINGS "US" +#elif defined(CONFIG_LWM2M_DEVICE_BINDING_UQS) +#define CONFIG_LWM2M_DEVICE_BINDINGS "UQS" +#else +#define CONFIG_LWM2M_DEVICE_BINDINGS "U" +#endif + +/** + * @brief Initialize the Device object. + * + * @param[in] client_data LwM2M client data. * - * @param[in] obj pointer to the device object + * @return Pointer to the Device object on success */ -void lwm2m_free_object_device(lwm2m_object_t *obj); +lwm2m_object_t *lwm2m_object_device_init(lwm2m_client_data_t *client_data); /** * @brief Determines if a reboot request has been issued to the device by a From 6109e588a57f1bc847d7f87f2a897cd62df3ab9f Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Wed, 3 Nov 2021 17:21:45 +0100 Subject: [PATCH 4/8] pkg/wakaama: add DTLS support --- pkg/wakaama/Makefile.dep | 10 + pkg/wakaama/Makefile.include | 1 + pkg/wakaama/contrib/lwm2m_client.c | 458 +++++++++++++----- pkg/wakaama/contrib/lwm2m_client_connection.c | 96 +++- pkg/wakaama/contrib/objects/security.c | 2 +- pkg/wakaama/include/lwm2m_client.h | 49 +- pkg/wakaama/include/lwm2m_client_connection.h | 7 +- pkg/wakaama/include/objects/device.h | 2 +- pkg/wakaama/include/objects/security.h | 6 +- 9 files changed, 492 insertions(+), 139 deletions(-) diff --git a/pkg/wakaama/Makefile.dep b/pkg/wakaama/Makefile.dep index e10f5bf102fc..f55a4a0fccd8 100644 --- a/pkg/wakaama/Makefile.dep +++ b/pkg/wakaama/Makefile.dep @@ -23,3 +23,13 @@ endif # wakaama uses Sock UDP (implemented by some stack) USEMODULE += sock_udp +USEMODULE += sock_async_event +USEMODULE += sock_util +USEMODULE += event_timeout +USEMODULE += event_thread +USEMODULE += event_thread_medium + +ifneq (,$(filter wakaama_client_dtls, $(USEMODULE))) + USEMODULE += sock_dtls + USEMODULE += credman_load +endif diff --git a/pkg/wakaama/Makefile.include b/pkg/wakaama/Makefile.include index 057bb48b72ec..42d24dab5c0d 100644 --- a/pkg/wakaama/Makefile.include +++ b/pkg/wakaama/Makefile.include @@ -18,3 +18,4 @@ ifneq (,$(or $(CONFIG_LWM2M_WITH_LOGS),$(filter -DCONFIG_LWM2M_WITH_LOGS=1,$(CFL endif PSEUDOMODULES += wakaama +PSEUDOMODULES += wakaama_client_dtls diff --git a/pkg/wakaama/contrib/lwm2m_client.c b/pkg/wakaama/contrib/lwm2m_client.c index 48a22260b7aa..6d8fd0448cc5 100644 --- a/pkg/wakaama/contrib/lwm2m_client.c +++ b/pkg/wakaama/contrib/lwm2m_client.c @@ -21,9 +21,17 @@ #include +#include "kernel_defines.h" #include "timex.h" - #include "liblwm2m.h" +#include "uri_parser.h" +#include "event/timeout.h" +#include "event/thread.h" +#include "net/sock/async/event.h" +#include "net/sock/util.h" +#include "objects/common.h" +#include "objects/security.h" +#include "objects/device.h" #include "lwm2m_platform.h" #include "lwm2m_client.h" @@ -34,26 +42,146 @@ #include "debug.h" /** - * @brief Determines if there has been a reboot request on the device object + * @brief Callback for event timeout that performs a step on the LwM2M FSM. + */ +static void _lwm2m_step_cb(event_t *arg); + +/** + * @brief Callback to handle UDP sock events. + */ +static void _udp_event_handler(sock_udp_t *sock, sock_async_flags_t type, void *arg); + +/** + * @brief Handle an incoming packet from a remote peer. + */ +static void _handle_packet_from_remote(const sock_udp_ep_t *remote, + lwm2m_client_connection_type_t type, uint8_t *buffer, + size_t len); + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +/** + * @brief Callback to handle DTLS sock events. + */ +static void _dtls_event_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg); + +/** + * @brief Try to find a server credential given an UDP endpoint. * - * @note This function is implemented in object_device.c + * @param[in] ep UDP endpoint to match + * @param[in] security_mode Security mode of the instance to find * - * @return true Reboot has been requested - * @return false Reboot has not been requested + * @return Tag of the credential to use when a suitable one is found + * @retval CREDMAN_TAG_EMPTY otherwise */ -bool lwm2m_device_reboot_requested(void); +static credman_tag_t _get_credential(const sock_udp_ep_t *ep, uint8_t security_mode); +#endif + +static event_t _lwm2m_step_event = { .handler = _lwm2m_step_cb }; +static event_timeout_t _lwm2m_step_event_timeout; +static lwm2m_client_data_t *_client_data = NULL; +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) /** - * @brief Thread with the main loop for receiving packets and stepping the LwM2M - * FSM. - * - * @param arg ignored + * @brief Callback registered to the client DTLS sock to select a PSK credential to use. */ -static void *_lwm2m_client_run(void *arg); +static credman_tag_t _client_psk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[], + unsigned tags_len, const char *hint, size_t hint_len) +{ + (void) sock; + (void) tags; + (void) tags_len; + (void) hint; + (void) hint_len; + + DEBUG("[lwm2m:client:PSK] getting credential\n"); + return _get_credential(ep, LWM2M_SECURITY_MODE_PRE_SHARED_KEY); +} + +static credman_tag_t _client_rpk_cb(sock_dtls_t *sock, sock_udp_ep_t *ep, credman_tag_t tags[], + unsigned tags_len) +{ + (void) sock; + (void) tags; + (void) tags_len; + + DEBUG("[lwm2m:client:RPK] getting credential\n"); + return _get_credential(ep, LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY); +} -static char _lwm2m_client_stack[THREAD_STACKSIZE_MAIN + - THREAD_EXTRA_STACKSIZE_PRINTF]; -static lwm2m_client_data_t *_client_data; +static credman_tag_t _get_credential(const sock_udp_ep_t *ep, uint8_t security_mode) +{ + lwm2m_object_t *sec = lwm2m_get_object_by_id(_client_data, LWM2M_SECURITY_OBJECT_ID); + if (!sec) { + DEBUG("[lwm2m:client] no security object found\n"); + return CREDMAN_TAG_EMPTY; + } + + /* prepare query */ + lwm2m_uri_t query_uri = { + .objectId = LWM2M_SECURITY_OBJECT_ID, + // .resourceId = LWM2M_SECURITY_URI_ID, + .flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID | LWM2M_URI_FLAG_RESOURCE_ID + }; + + lwm2m_list_t *instance = sec->instanceList; + + /* check all registered security object instances */ + while (instance) { + query_uri.instanceId = instance->id; + + /* first check the security mode */ + query_uri.resourceId = LWM2M_SECURITY_SECURITY_ID; + int64_t mode; + int res = lwm2m_get_int(_client_data, &query_uri, &mode); + if (res < 0) { + DEBUG("[lwm2m:client] could not get security mode of %" PRIu16 "\n", instance->id); + goto check_next; + } + + if (mode != security_mode) { + goto check_next; + } + + /* if security mode matches, check the URI */ + char uri[CONFIG_LWM2M_URI_MAX_SIZE] = { 0 }; + size_t uri_len = sizeof(uri); + res = lwm2m_get_string(_client_data, &query_uri, uri, &uri_len); + if (res < 0) { + DEBUG("[lwm2m:client] could not get URI of %" PRIu16 "\n", instance->id); + goto check_next; + } + + sock_udp_ep_t inst_ep; + uri_parser_result_t parsed_uri; + res = uri_parser_process_string(&parsed_uri, uri); + if (res < 0) { + DEBUG("[lwm2m:client] could not parse URI\n"); + goto check_next; + } + + res = sock_udp_str2ep(&inst_ep, parsed_uri.host); + if (res < 0) { + DEBUG("[lwm2m:client] could not convert URI to EP (%s)\n", parsed_uri.host); + goto check_next; + } + + if (sock_udp_ep_equal(ep, &inst_ep)) { + credman_tag_t tag = lwm2m_object_security_get_credential(instance->id); + + DEBUG("[lwm2m:client:PSK] found matching EP on instance %" PRIu16 "\n", instance->id); + DEBUG("[lwm2m:client:PSK] tag: %" PRIu16 "\n", tag); + + return tag; + } + +check_next: + instance = instance->next; + } + + return CREDMAN_TAG_EMPTY; +} + +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ void lwm2m_client_init(lwm2m_client_data_t *client_data) { @@ -73,11 +201,36 @@ lwm2m_context_t *lwm2m_client_run(lwm2m_client_data_t *client_data, /* create sock for UDP server */ _client_data->local_ep.port = atoi(CONFIG_LWM2M_LOCAL_PORT); - if (sock_udp_create(&_client_data->sock, &_client_data->local_ep, NULL, 0)) { + if (sock_udp_create(&_client_data->sock, &_client_data->local_ep, NULL, 0) < 0) { DEBUG("[lwm2m_client_run] Can't create server socket\n"); return NULL; } +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + /* create sock for DTLS server */ + _client_data->dtls_local_ep.family = AF_INET6; + _client_data->dtls_local_ep.netif = SOCK_ADDR_ANY_NETIF; + _client_data->dtls_local_ep.port = atoi(CONFIG_LWM2M_LOCAL_DTLS_PORT); + res = sock_udp_create(&_client_data->dtls_udp_sock, &_client_data->dtls_local_ep, NULL, 0); + if (res < 0) { + DEBUG("[lwm2m_client_run] Can't create DTLS server UDP sock\n"); + return NULL; + } + + res = sock_dtls_create(&_client_data->dtls_sock, &_client_data->dtls_udp_sock, + CREDMAN_TAG_EMPTY, SOCK_DTLS_1_2, SOCK_DTLS_CLIENT); + if (res < 0) { + DEBUG("[lwm2m_client_run] Can't create DTLS server sock\n"); + sock_udp_close(&_client_data->dtls_udp_sock); + sock_udp_close(&_client_data->sock); + return NULL; + } + + /* register callback for credential selection */ + sock_dtls_set_client_psk_cb(&_client_data->dtls_sock, _client_psk_cb); + sock_dtls_set_rpk_cb(&_client_data->dtls_sock, _client_rpk_cb); +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + /* initiate LwM2M */ _client_data->lwm2m_ctx = lwm2m_init(_client_data); if (!_client_data->lwm2m_ctx) { @@ -92,115 +245,200 @@ lwm2m_context_t *lwm2m_client_run(lwm2m_client_data_t *client_data, return NULL; } - _client_data->pid = thread_create(_lwm2m_client_stack, - sizeof(_lwm2m_client_stack), - THREAD_PRIORITY_MAIN - 1, - THREAD_CREATE_STACKTEST, - _lwm2m_client_run, - NULL, - "LwM2M client"); +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + lwm2m_client_refresh_dtls_credentials(); +#endif + + sock_udp_event_init(&_client_data->sock, EVENT_PRIO_MEDIUM, _udp_event_handler, NULL); + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + sock_dtls_event_init(&_client_data->dtls_sock, EVENT_PRIO_MEDIUM, _dtls_event_handler, NULL); +#endif + + /* periodic event to tick the wakaama state machine */ + event_timeout_init(&_lwm2m_step_event_timeout, EVENT_PRIO_MEDIUM, &_lwm2m_step_event); + event_timeout_set(&_lwm2m_step_event_timeout, LWM2M_CLIENT_MIN_REFRESH_TIME); + return _client_data->lwm2m_ctx; } -static void *_lwm2m_client_run(void *arg) +static void _handle_packet_from_remote(const sock_udp_ep_t *remote, + lwm2m_client_connection_type_t type, uint8_t *buffer, + size_t len) +{ + lwm2m_client_connection_t *conn; + + DEBUG("[lwm2m:client] finding connection\n"); + conn = lwm2m_client_connection_find(_client_data->conn_list, remote, type); + + if (conn) { + DEBUG("[lwm2m:client] handle packet (%i bytes)\n", (int)len); + int result = lwm2m_connection_handle_packet(conn, buffer, len, _client_data); + if (0 != result) { + DEBUG("[lwm2m:client] error handling message %i\n", result); + } + } + else { + DEBUG("[lwm2m:client] couldn't find incoming connection\n"); + } +} + +static void _udp_event_handler(sock_udp_t *sock, sock_async_flags_t type, void *arg) { (void) arg; - time_t reboot_time = 0; - while (1) { - time_t tv = LWM2M_CLIENT_MIN_REFRESH_TIME; + + sock_udp_ep_t remote; + + if (type & SOCK_ASYNC_MSG_RECV) { uint8_t rcv_buf[LWM2M_CLIENT_RCV_BUFFER_SIZE]; - ssize_t rcv_len = sizeof(rcv_buf); - sock_udp_ep_t remote; + ssize_t rcv_len = sock_udp_recv(sock, rcv_buf, sizeof(rcv_buf), 0, &remote); + if (rcv_len <= 0) { + DEBUG("[lwm2m:client] UDP receive failure: %i\n", (int)rcv_len); + return; + } + + _handle_packet_from_remote(&remote, LWM2M_CLIENT_CONN_UDP, rcv_buf, rcv_len); + } +} - if (lwm2m_device_reboot_requested()) { - time_t tv_sec; +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +static void _dtls_event_handler(sock_dtls_t *sock, sock_async_flags_t type, void *arg) +{ + (void) arg; - tv_sec = lwm2m_gettime(); + sock_udp_ep_t remote; + sock_dtls_session_t dtls_remote; + uint8_t rcv_buf[LWM2M_CLIENT_RCV_BUFFER_SIZE]; - if (0 == reboot_time) { - DEBUG("reboot requested; rebooting in %u seconds\n", - LWM2M_CLIENT_REBOOT_TIME); - reboot_time = tv_sec + LWM2M_CLIENT_REBOOT_TIME; - } - if (reboot_time < tv_sec) { - DEBUG("reboot time expired, rebooting ...\n"); - pm_reboot(); - } - else { - tv = reboot_time - tv_sec; - } + if (type & SOCK_ASYNC_MSG_RECV) { + ssize_t rcv_len = sock_dtls_recv(sock, &dtls_remote, rcv_buf, sizeof(rcv_buf), 0); + if (rcv_len <= 0) { + DEBUG("[lwm2m:client] DTLS receive failure: %i\n", (int)rcv_len); + return; } - /* - * This function does two things: - * - first it does the work needed by liblwm2m (eg. (re)sending some - * packets). - * - Secondly it adjusts the timeout value (default 60s) depending on the - * state of the transaction - * (eg. retransmission) and the time between the next operation - */ - lwm2m_step(_client_data->lwm2m_ctx, &tv); - DEBUG(" -> State: "); - switch (_client_data->lwm2m_ctx->state) { - case STATE_INITIAL: - DEBUG("STATE_INITIAL\n"); - break; - case STATE_BOOTSTRAP_REQUIRED: - DEBUG("STATE_BOOTSTRAP_REQUIRED\n"); - break; - case STATE_BOOTSTRAPPING: - DEBUG("STATE_BOOTSTRAPPING\n"); - break; - case STATE_REGISTER_REQUIRED: - DEBUG("STATE_REGISTER_REQUIRED\n"); - break; - case STATE_REGISTERING: - DEBUG("STATE_REGISTERING\n"); - break; - case STATE_READY: - DEBUG("STATE_READY\n"); - if (tv > LWM2M_CLIENT_MIN_REFRESH_TIME) { - tv = LWM2M_CLIENT_MIN_REFRESH_TIME; - } - break; - default: - DEBUG("Unknown...\n"); - break; - } + sock_dtls_session_get_udp_ep(&dtls_remote, &remote); - sock_udp_ep_t local; - int res = sock_udp_get_local(&_client_data->sock, &local); - /* avoid compilation errors if NDEBUG is enabled */ - (void)res; - assert(res >= 0); - DEBUG("Waiting for UDP packet on port: %d\n", local.port); - rcv_len = sock_udp_recv(&_client_data->sock, rcv_buf, sizeof(rcv_buf), - tv * US_PER_SEC, &remote); - DEBUG("sock_udp_recv()\n"); - if (rcv_len > 0) { - DEBUG("Finding connection\n"); - lwm2m_client_connection_t *conn = lwm2m_client_connection_find( - _client_data->conn_list, &remote); - if (conn) { - DEBUG("lwm2m_connection_handle_packet(%" PRIiSIZE ")\n", rcv_len); - int result = lwm2m_connection_handle_packet(conn, rcv_buf, - rcv_len, - _client_data); - if (0 != result) { - DEBUG("error handling message %i\n", result); - } - } - else { - DEBUG("Could not find incoming connection\n"); + _handle_packet_from_remote(&remote, LWM2M_CLIENT_CONN_DTLS, rcv_buf, rcv_len); + } +} +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + +static void _lwm2m_step_cb(event_t *arg) +{ + (void) arg; + time_t next_step = LWM2M_CLIENT_MIN_REFRESH_TIME; + + /* check if we need to reboot */ + if (lwm2m_device_reboot_requested()) { + DEBUG("[lwm2m:client] reboot requested, rebooting ...\n"); + pm_reboot(); + } + + /* perform step on the LwM2M FSM */ + lwm2m_step(_client_data->lwm2m_ctx, &next_step); + DEBUG("[lwm2m:client] state: "); + switch (_client_data->lwm2m_ctx->state) { + case STATE_INITIAL: + DEBUG("STATE_INITIAL\n"); + break; + case STATE_BOOTSTRAP_REQUIRED: + DEBUG("STATE_BOOTSTRAP_REQUIRED\n"); + break; + case STATE_BOOTSTRAPPING: + DEBUG("STATE_BOOTSTRAPPING\n"); + break; + case STATE_REGISTER_REQUIRED: + DEBUG("STATE_REGISTER_REQUIRED\n"); + break; + case STATE_REGISTERING: + DEBUG("STATE_REGISTERING\n"); + break; + case STATE_READY: + DEBUG("STATE_READY\n"); + if (next_step > LWM2M_CLIENT_MIN_REFRESH_TIME) { + next_step = LWM2M_CLIENT_MIN_REFRESH_TIME; } + break; + default: + DEBUG("Unknown...\n"); + break; + } + + /* program next step */ + event_timeout_set(&_lwm2m_step_event_timeout, next_step * US_PER_SEC); +} + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +void lwm2m_client_add_credential(credman_tag_t tag) +{ + if (!_client_data) { + return; + } + + const credman_tag_t *creds; + size_t creds_len = sock_dtls_get_credentials(&_client_data->dtls_sock, &creds); + + DEBUG("[lwm2m:client] trying to add credential with tag %" PRIu16 "\n", tag); + for (unsigned i = 0; i < creds_len; i++) { + if (creds[i] == tag) { + return; } - else if ((rcv_len < 0) && - ((rcv_len != -EAGAIN) && (rcv_len != -ETIMEDOUT))) { - DEBUG("Unexpected sock_udp_recv error code %" PRIiSIZE "\n", rcv_len); + } + + sock_dtls_add_credential(&_client_data->dtls_sock, tag); + DEBUG("[lwm2m:client] added\n"); +} + +void lwm2m_client_remove_credential(credman_tag_t tag) +{ + DEBUG("[lwm2m:client] removing credential with tag %" PRIu16 "\n", tag); + sock_dtls_remove_credential(&_client_data->dtls_sock, tag); +} + +void lwm2m_client_refresh_dtls_credentials(void) +{ + if (!_client_data) { + return; + } + + DEBUG("[lwm2m:client:refresh_cred] refreshing DTLS credentials\n"); + + lwm2m_object_t *sec = lwm2m_get_object_by_id(_client_data, LWM2M_SECURITY_OBJECT_ID); + if (!sec) { + DEBUG("[lwm2m:client:refresh_cred] no security object found\n"); + return; + } + + /* prepare query */ + lwm2m_uri_t query_uri = { + .objectId = LWM2M_SECURITY_OBJECT_ID, + .flag = LWM2M_URI_FLAG_OBJECT_ID | LWM2M_URI_FLAG_INSTANCE_ID | LWM2M_URI_FLAG_RESOURCE_ID + }; + + lwm2m_list_t *instance = sec->instanceList; + int64_t val; + + /* check all registered security object instances */ + do { + /* get the security mode */ + query_uri.instanceId = instance->id; + query_uri.resourceId = LWM2M_SECURITY_SECURITY_ID; + + int res = lwm2m_get_int(_client_data, &query_uri, &val); + if (res < 0) { + DEBUG("[lwm2m:client:refresh_cred] could not get security mode of %" PRIu16 "\n", + instance->id); + continue; } - else { - DEBUG("UDP error code: %" PRIiSIZE "\n", rcv_len); + + if (val == LWM2M_SECURITY_MODE_PRE_SHARED_KEY || + val == LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY) { + credman_tag_t tag = lwm2m_object_security_get_credential(instance->id); + if (tag != CREDMAN_TAG_EMPTY) { + lwm2m_client_add_credential(tag); + } } - } - return NULL; + } while ((instance = instance->next) != NULL); } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ diff --git a/pkg/wakaama/contrib/lwm2m_client_connection.c b/pkg/wakaama/contrib/lwm2m_client_connection.c index 4090902f0066..ac3349bda7f3 100644 --- a/pkg/wakaama/contrib/lwm2m_client_connection.c +++ b/pkg/wakaama/contrib/lwm2m_client_connection.c @@ -39,11 +39,18 @@ */ #include +#include #include "kernel_defines.h" #include "net/netif.h" #include "uri_parser.h" #include "liblwm2m.h" +#include "net/sock/udp.h" + +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +#include "net/sock/dtls.h" +#endif + #include "lwm2m_client.h" #include "lwm2m_client_config.h" #include "lwm2m_client_connection.h" @@ -88,7 +95,7 @@ static int _connection_send(lwm2m_client_connection_t *conn, uint8_t *buffer, * @param[in] iface_len interface string length * * @return pointer to the interface to use on success - * @return NULL on error + * @retval NULL on error */ static netif_t *_get_interface(const char *iface, size_t len); @@ -97,7 +104,7 @@ void *lwm2m_connect_server(uint16_t sec_obj_inst_id, void *user_data) lwm2m_client_data_t *client_data = (lwm2m_client_data_t *)user_data; lwm2m_client_connection_t *new_conn; - DEBUG("[lwm2m_connect_server] Connecting to server in security instance %d\n", sec_obj_inst_id); + DEBUG("[lwm2m_connect_server] Connecting to server in security instance %" PRIu16 "\n", sec_obj_inst_id); new_conn = _connection_create(sec_obj_inst_id, client_data); if (new_conn) { @@ -170,9 +177,9 @@ uint8_t lwm2m_buffer_send(void *sessionH, uint8_t *buffer, size_t length, return COAP_NO_ERROR; } -lwm2m_client_connection_t *lwm2m_client_connection_find( - lwm2m_client_connection_t *conn_list, - const sock_udp_ep_t *remote) +lwm2m_client_connection_t *lwm2m_client_connection_find(lwm2m_client_connection_t *conn_list, + const sock_udp_ep_t *remote, + lwm2m_client_connection_type_t type) { lwm2m_client_connection_t *conn = conn_list; @@ -180,7 +187,7 @@ lwm2m_client_connection_t *lwm2m_client_connection_find( uint8_t ip_len = 128; ipv6_addr_to_str(ip, (ipv6_addr_t *)&remote->addr.ipv6, ip_len); - DEBUG("Looking for connection from [%s]:%d\n", ip, remote->port); + DEBUG("Looking for connection from [%s]:%" PRIu16 "\n", ip, remote->port); if (conn_list == NULL) { DEBUG("Conn list is null!"); @@ -188,8 +195,8 @@ lwm2m_client_connection_t *lwm2m_client_connection_find( while (conn != NULL) { ipv6_addr_to_str(ip, (ipv6_addr_t *)&conn->remote.addr.ipv6, ip_len); - DEBUG("Comparing to [%s]:%d\n", ip, conn->remote.port); - if ((conn->remote.port == remote->port) && + DEBUG("Comparing to [%s]:%" PRIu16 "\n", ip, conn->remote.port); + if ((conn->remote.port == remote->port) && conn->type == type && ipv6_addr_equal((ipv6_addr_t *)&(conn->remote.addr.ipv6), (ipv6_addr_t *)&(remote->addr.ipv6))) { break; @@ -199,9 +206,8 @@ lwm2m_client_connection_t *lwm2m_client_connection_find( return conn; } -int lwm2m_connection_handle_packet(lwm2m_client_connection_t *conn, - uint8_t *buffer, size_t num_bytes, - lwm2m_client_data_t *client_data) +int lwm2m_connection_handle_packet(lwm2m_client_connection_t *conn, uint8_t *buffer, + size_t num_bytes, lwm2m_client_data_t *client_data) { lwm2m_handle_packet(client_data->lwm2m_ctx, buffer, num_bytes, conn); return 0; @@ -211,12 +217,26 @@ static int _connection_send(lwm2m_client_connection_t *conn, uint8_t *buffer, size_t buffer_size, lwm2m_client_data_t *client_data) { - ssize_t sent_bytes = sock_udp_send(&(client_data->sock), buffer, - buffer_size, &(conn->remote)); - if (sent_bytes <= 0) { - DEBUG("[_connection_send] Could not send UDP packet: %" PRIiSIZE "\n", sent_bytes); - return -1; + DEBUG("[_connection_send] trying to send %" PRIiSIZE " bytes\n", buffer_size); + if (conn->type == LWM2M_CLIENT_CONN_UDP) { + ssize_t sent_bytes = sock_udp_send(&(client_data->sock), buffer, + buffer_size, &(conn->remote)); + if (sent_bytes <= 0) { + DEBUG("[_connection_send] Could not send UDP packet: %i\n", (int)sent_bytes); + return -1; + } + } +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + else { + ssize_t sent_bytes = sock_dtls_send(&client_data->dtls_sock, &conn->session, buffer, + buffer_size, SOCK_NO_TIMEOUT); + if (sent_bytes <= 0) { + DEBUG("[_connection_send] Could not send DTLS packet: %i\n", (int)sent_bytes); + return -1; + } } +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + conn->last_send = lwm2m_gettime(); return 0; } @@ -225,7 +245,8 @@ static netif_t *_get_interface(const char *iface, size_t iface_len) { netif_t *netif = NULL; - if (iface == NULL) { + if (!iface || !iface_len) { + DEBUG("[lwm2m:client] no interface defined in host\n"); /* get the number of net interfaces */ unsigned netif_numof = 0; while ((netif = netif_iter(netif)) != NULL) { @@ -240,6 +261,7 @@ static netif_t *_get_interface(const char *iface, size_t iface_len) } } else { + DEBUG("[lwm2m:client] getting interface by name %.*s\n", (unsigned)iface_len, iface); netif = netif_get_by_name_buffer(iface, iface_len); } @@ -250,7 +272,7 @@ static lwm2m_client_connection_t *_connection_create(uint16_t sec_obj_inst_id, lwm2m_client_data_t *client_data) { lwm2m_client_connection_t *conn = NULL; - char uri[MAX_URI_LENGTH]; + char uri[MAX_URI_LENGTH] = { 0 }; size_t uri_len = ARRAY_SIZE(uri); uint16_t port; bool is_bootstrap; @@ -299,7 +321,7 @@ static lwm2m_client_connection_t *_connection_create(uint16_t sec_obj_inst_id, port = parsed_uri.port; } - DEBUG("[_connection_create] Creating connection to Host: %.*s, Port: %u\n", + DEBUG("[_connection_create] Creating connection to Host: %.*s, Port: %" PRIu16 "\n", parsed_uri.ipv6addr_len, parsed_uri.ipv6addr, port); /* allocate new connection */ @@ -331,7 +353,9 @@ static lwm2m_client_connection_t *_connection_create(uint16_t sec_obj_inst_id, * is only one defined. */ if (ipv6_addr_is_link_local((ipv6_addr_t *)&conn->remote.addr.ipv6)) { netif_t *netif = _get_interface(parsed_uri.zoneid, parsed_uri.zoneid_len); + if (netif == NULL) { + DEBUG("[lwm2m:client] could not determine an interface to use\n"); goto free_out; } else { @@ -343,6 +367,40 @@ static lwm2m_client_connection_t *_connection_create(uint16_t sec_obj_inst_id, } } +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + uint8_t buf[DTLS_HANDSHAKE_BUFSIZE]; + int64_t val; + resource_uri.resourceId = LWM2M_SECURITY_SECURITY_ID; + res = lwm2m_get_int(client_data, &resource_uri, &val); + if (res < 0) { + DEBUG("[lwm2m:client] could not get security instance mode\n"); + goto free_out; + } + + if (val == LWM2M_SECURITY_MODE_PRE_SHARED_KEY || val == LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY) { + conn->type = LWM2M_CLIENT_CONN_DTLS; + DEBUG("[lwm2m:client] DTLS session init\n"); + res = sock_dtls_session_init(&client_data->dtls_sock, &conn->remote, &conn->session); + if (res <= 0) { + DEBUG("[lwm2m:client] could not initiate DTLS session\n"); + goto free_out; + } + + DEBUG("[lwm2m:client] receiving DTLS handshake\n"); + res = sock_dtls_recv(&client_data->dtls_sock, &conn->session, buf, sizeof(buf), US_PER_SEC); + if (res != -SOCK_DTLS_HANDSHAKE) { + DEBUG("[lwm2m:client] error creating session: %i\n", res); + goto free_out; + } + DEBUG("[lwm2m:client] connection to server successful\n"); + } + else { + conn->type = LWM2M_CLIENT_CONN_UDP; + } +#else + conn->type = LWM2M_CLIENT_CONN_UDP; +#endif /* MODULE_WAKAAMA_CLIENT_DTLS */ + conn->last_send = lwm2m_gettime(); goto out; diff --git a/pkg/wakaama/contrib/objects/security.c b/pkg/wakaama/contrib/objects/security.c index b4765130b004..3bc28974604c 100644 --- a/pkg/wakaama/contrib/objects/security.c +++ b/pkg/wakaama/contrib/objects/security.c @@ -185,7 +185,7 @@ static int _get_value(lwm2m_data_t *data, lwm2m_obj_security_inst_t *instance); /** * @brief Initialize a new instance with the given arguments. - * + * * @param instance[out] Instance to initialize. * @param instance_id[in] ID of the instance. * @param args[in] Arguments to initialize the instance with. diff --git a/pkg/wakaama/include/lwm2m_client.h b/pkg/wakaama/include/lwm2m_client.h index 39c1f5b19dc9..49c1e21c76db 100644 --- a/pkg/wakaama/include/lwm2m_client.h +++ b/pkg/wakaama/include/lwm2m_client.h @@ -36,16 +36,32 @@ extern "C" { #include "periph/pm.h" #include "net/sock/udp.h" +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) +#include "net/sock/dtls.h" +#endif + #include "lwm2m_client_config.h" #include "liblwm2m.h" +/** + * @brief Type of connection to the LwM2M server. + */ +typedef enum { + LWM2M_CLIENT_CONN_UDP, /**< UDP */ + LWM2M_CLIENT_CONN_DTLS /**< DTLS over UDP */ +} lwm2m_client_connection_type_t; + /** * @brief Connection to server descriptor */ typedef struct lwm2m_client_connection { struct lwm2m_client_connection *next; /**< pointer to the next connection */ - sock_udp_ep_t remote; /**< remote endpoint */ - time_t last_send; /**< last sent packet to the server */ + sock_udp_ep_t remote; /**< remote endpoint */ +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) || DOXYGEN + sock_dtls_session_t session; /**< DTLS session (needs wakaama_client_dtls module) */ +#endif + lwm2m_client_connection_type_t type; /**< type of connection */ + time_t last_send; /**< last sent packet to the server */ } lwm2m_client_connection_t; /** @@ -54,6 +70,11 @@ typedef struct lwm2m_client_connection { typedef struct { kernel_pid_t pid; /**< PID of the client thread */ sock_udp_t sock; /**< UDP server sock */ +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) + sock_udp_t dtls_udp_sock; /**< UDP sock for DTLS */ + sock_dtls_t dtls_sock; /**< DTLS client sock */ + sock_udp_ep_t dtls_local_ep; /**< DTLS local endpoint */ +#endif sock_udp_ep_t local_ep; /**< Local endpoint */ lwm2m_context_t *lwm2m_ctx; /**< LwM2M context */ lwm2m_client_connection_t *conn_list; /**< LwM2M connections list */ @@ -114,6 +135,30 @@ static inline lwm2m_context_t *lwm2m_client_get_ctx( return client_data->lwm2m_ctx; } +#if IS_USED(MODULE_WAKAAMA_CLIENT_DTLS) || defined(DOXYGEN) +/** + * @brief Refreshes the client available credentials using the currently registered security objects + * @note Only available when using the module `wakaama_client_dtls`. + */ +void lwm2m_client_refresh_dtls_credentials(void); + +/** + * @brief Adds a credential tag to be used with the LwM2M DTLS sock. + * + * If the tag is already available it will not be added again. + * @note Only available when using the module `wakaama_client_dtls`. + * @param[in] tag Tag to add. + */ +void lwm2m_client_add_credential(credman_tag_t tag); + +/** + * @brief Removes a credential tag from the available to use with the LwM2M DTLS sock. + * @note Only available when using the module `wakaama_client_dtls`. + * @param[in] tag Tag to remove. + */ +void lwm2m_client_remove_credential(credman_tag_t tag); +#endif /* MODULE_WAKAAMA_CLIENT_DTLS || DOXYGEN */ + #ifdef __cplusplus } #endif diff --git a/pkg/wakaama/include/lwm2m_client_connection.h b/pkg/wakaama/include/lwm2m_client_connection.h index e6be584ad7fa..4d2a335bbc13 100644 --- a/pkg/wakaama/include/lwm2m_client_connection.h +++ b/pkg/wakaama/include/lwm2m_client_connection.h @@ -58,13 +58,14 @@ extern "C" { * * @param[in] conn_list connections list to search * @param[in] remote remote UDP endpoint to compare to + * @param[in] type type of connection to look for * * @return pointer to the connection in success * @return NULL otherwise */ -lwm2m_client_connection_t *lwm2m_client_connection_find( - lwm2m_client_connection_t *conn_list, - const sock_udp_ep_t *remote); +lwm2m_client_connection_t *lwm2m_client_connection_find(lwm2m_client_connection_t *conn_list, + const sock_udp_ep_t *remote, + lwm2m_client_connection_type_t type); /** * @brief Handles a received packet from a connection diff --git a/pkg/wakaama/include/objects/device.h b/pkg/wakaama/include/objects/device.h index 3f29f134ca53..5cf1bb06259c 100644 --- a/pkg/wakaama/include/objects/device.h +++ b/pkg/wakaama/include/objects/device.h @@ -229,7 +229,7 @@ enum lwm2m_device_error_codes { /** * @brief Device binding and queue mode - * + * * @note Select using CONFIG_LWM2M_DEVICE_BINDING_* */ #if defined(CONFIG_LWM2M_DEVICE_BINDING_U) diff --git a/pkg/wakaama/include/objects/security.h b/pkg/wakaama/include/objects/security.h index 87fa0b15a555..0ae70e2651da 100644 --- a/pkg/wakaama/include/objects/security.h +++ b/pkg/wakaama/include/objects/security.h @@ -74,7 +74,7 @@ * To use this security mode the following keys are required: * - server public key (`SubjectPublicKeyInfo` DER encoded, according to RFC5280) * - own public (`SubjectPublicKeyInfo` DER encoded) and private (as a `ECPrivateKey` DER encoded - * sequence, according to RFC5915)keys. See bellow on how they can be generated. + * sequence, according to RFC5915)keys. See below on how they can be generated. * * It is possible that you may need to increase @ref CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP when using * RPK mode. @@ -119,7 +119,7 @@ * ~~~~~~~~~~~~~~~~~~~~ * $ openssl ec -in keys.der -inform DER -outform DER -pubout | xxd -i * ~~~~~~~~~~~~~~~~~~~~ - * + * * 3. Get the private part of the key: * ~~~~~~~~~~~~~~~~~~~~ * $ openssl ec -in keys.der -inform DER -no_public -outform DER | xxd -i @@ -264,7 +264,7 @@ extern "C" { #define LWM2M_SECURITY_HOLD_OFF_ID 11 /** - * @brief Boostrap server account timeout + * @brief Bootstrap server account timeout */ #define LWM2M_SECURITY_BOOTSTRAP_TIMEOUT_ID 12 /** @} */ From c0559f68feb7b6d587bb5ca62157d7327f553763 Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Thu, 25 Mar 2021 13:47:08 +0100 Subject: [PATCH 5/8] pkg/wakaama: patch cast of COAP macro to time_t --- ...ast-COAP_EXCHANGE_LIFETIME-to-time_t.patch | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 pkg/wakaama/patches/0022-er-coap-13-cast-COAP_EXCHANGE_LIFETIME-to-time_t.patch diff --git a/pkg/wakaama/patches/0022-er-coap-13-cast-COAP_EXCHANGE_LIFETIME-to-time_t.patch b/pkg/wakaama/patches/0022-er-coap-13-cast-COAP_EXCHANGE_LIFETIME-to-time_t.patch new file mode 100644 index 000000000000..d047acfb0f48 --- /dev/null +++ b/pkg/wakaama/patches/0022-er-coap-13-cast-COAP_EXCHANGE_LIFETIME-to-time_t.patch @@ -0,0 +1,25 @@ +From 92a5ae1f4289715546340ffcb31ac24e94a33efa Mon Sep 17 00:00:00 2001 +From: Leandro Lanzieri +Date: Thu, 25 Mar 2021 13:12:49 +0100 +Subject: [PATCH] er-coap-13: cast COAP_EXCHANGE_LIFETIME to time_t + +--- + core/er-coap-13/er-coap-13.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/core/er-coap-13/er-coap-13.h b/core/er-coap-13/er-coap-13.h +index 495f49e..21af2af 100644 +--- a/core/er-coap-13/er-coap-13.h ++++ b/core/er-coap-13/er-coap-13.h +@@ -62,7 +62,7 @@ + + #define COAP_MAX_TRANSMIT_WAIT ((COAP_RESPONSE_TIMEOUT * ( (1 << (COAP_MAX_RETRANSMIT + 1) ) - 1) * COAP_ACK_RANDOM_FACTOR)) + #define COAP_MAX_TRANSMIT_SPAN ((COAP_RESPONSE_TIMEOUT * ( (1 << COAP_MAX_RETRANSMIT) - 1) * COAP_ACK_RANDOM_FACTOR)) +-#define COAP_EXCHANGE_LIFETIME (COAP_MAX_TRANSMIT_SPAN + (2 * COAP_MAX_LATENCY) + COAP_PROCESSING_DELAY) ++#define COAP_EXCHANGE_LIFETIME (time_t)(COAP_MAX_TRANSMIT_SPAN + (2 * COAP_MAX_LATENCY) + COAP_PROCESSING_DELAY) + + #define COAP_HEADER_LEN 4 /* | version:0x03 type:0x0C tkl:0xF0 | code | mid:0x00FF | mid:0xFF00 | */ + #define COAP_ETAG_LEN 8 /* The maximum number of bytes for the ETag */ +-- +2.31.0 + From 0f26003530327c404825d660dd4edf7e0c4fbc4a Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Thu, 25 Mar 2021 13:51:08 +0100 Subject: [PATCH 6/8] examples/lwm2m: update to new API and use DTLS --- examples/lwm2m/Kconfig | 44 ++++++++++++++++++++ examples/lwm2m/Makefile | 35 ++++++++++------ examples/lwm2m/Makefile.ci | 13 ++++++ examples/lwm2m/README.md | 41 +++++++++++-------- examples/lwm2m/app.config | 2 + examples/lwm2m/credentials.h | 79 ++++++++++++++++++++++++++++++++++++ examples/lwm2m/lwm2m_cli.c | 64 +++++++++++++++++++++++++---- 7 files changed, 241 insertions(+), 37 deletions(-) create mode 100644 examples/lwm2m/Kconfig create mode 100644 examples/lwm2m/app.config create mode 100644 examples/lwm2m/credentials.h diff --git a/examples/lwm2m/Kconfig b/examples/lwm2m/Kconfig new file mode 100644 index 000000000000..9e3b65c815a9 --- /dev/null +++ b/examples/lwm2m/Kconfig @@ -0,0 +1,44 @@ +# Copyright (c) 2021 HAW Hamburg +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menu "Application configuration" + +config LWM2M_SERVER_URI + string "LwM2M Server URI to register/bootstrap with" + default "coap://[fd00:dead:beef::1]:5684" + help + The host part of the URI MUST be a valid IPv6 address. + +config LWM2M_SERVER_SHORT_ID + int "Server Short ID" + default 1 + range 1 65534 + +choice + bool "Credential type" + +menuconfig LWM2M_CRED_PSK + bool "PSK (Pre-shared keys)" + select DTLS_PSK + +if LWM2M_CRED_PSK + config LWM2M_PSK_ID + string "PSK Identity" + default "Client_Identity" + + config LWM2M_PSK_KEY + string "PSK Key" + default "ThisIsRIOT!" +endif # LWM2M_CRED_PSK + +config LWM2M_CRED_RPK + bool "RPK (Raw public keys)" + select DTLS_ECC + +endchoice + +endmenu # Application configuration diff --git a/examples/lwm2m/Makefile b/examples/lwm2m/Makefile index a214c2079cbe..5945c0628609 100644 --- a/examples/lwm2m/Makefile +++ b/examples/lwm2m/Makefile @@ -1,5 +1,5 @@ # name of your application -APPLICATION = wakaama +APPLICATION = lwm2m # If no BOARD is found in the environment, use this default: BOARD ?= native @@ -7,8 +7,6 @@ BOARD ?= native # This has to be the absolute path to the RIOT base directory: RIOTBASE ?= $(CURDIR)/../.. - - # Include packages that pull up and auto-init the link layer. # NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present USEMODULE += netdev_default @@ -27,21 +25,34 @@ USEMODULE += ps # development process: DEVELHELP ?= 1 -# Specific the server URI address (NOTE: Domain names not supported yet) -SERVER_URI ?= '"coap://[fd00:dead:beef::1]"' - # NOTE: Add the package for wakaama USEPKG += wakaama USEMODULE += wakaama_objects_light_control -# Uncomment to enable Wakaama debug log -#CFLAGS += -DCONFIG_LWM2M_WITH_LOGS=1 -# Uncomment to indicate that the server is a LwM2M bootstrap server -# CFLAGS += -DCONFIG_LWM2M_BOOTSTRAP=1 +# add DTLS support +USEMODULE += wakaama_client_dtls + +# Specify sock DTLS implementation +USEPKG += tinydtls +# tinydtls needs crypto secure PRNG +USEMODULE += prng_sha1prng + +## Application-specific Configuration options +LWM2M_SERVER_URI ?= '"coap://[fd00:dead:beef::1]:5684"' +LWM2M_SERVER_SHORT_ID ?= 1 + +CFLAGS += -DEVENT_THREAD_MEDIUM_STACKSIZE='(3*1024)' include $(RIOTBASE)/Makefile.include -# Configure server via CFLAGS only if not done via Kconfig +DTLS_MAX_BUF=1024 + +# Configure via CFLAGS only if not done via Kconfig ifndef CONFIG_LWM2M_SERVER_URI - CFLAGS += -DCONFIG_LWM2M_SERVER_URI=$(SERVER_URI) + CFLAGS += -DCONFIG_LWM2M_SERVER_URI=$(LWM2M_SERVER_URI) + CFLAGS += -DCONFIG_LWM2M_SERVER_SHORT_ID=$(LWM2M_SERVER_SHORT_ID) + CFLAGS += -DCONFIG_DTLS_PEER_MAX=2 + CFLAGS += -DCONFIG_MAX_BUF=1024 + # Uncomment to enable Wakaama debug log + #CFLAGS += -DCONFIG_LWM2M_WITH_LOGS=1 endif diff --git a/examples/lwm2m/Makefile.ci b/examples/lwm2m/Makefile.ci index c116a7e4f4c1..dde0c7ef7a5b 100644 --- a/examples/lwm2m/Makefile.ci +++ b/examples/lwm2m/Makefile.ci @@ -1,5 +1,6 @@ BOARD_INSUFFICIENT_MEMORY := \ airfy-beacon \ + arduino-mkr1000 \ b-l072z-lrwan1 \ blackpill-stm32f103c8 \ blackpill-stm32f103cb \ @@ -7,8 +8,13 @@ BOARD_INSUFFICIENT_MEMORY := \ bluepill-stm32f103c8 \ bluepill-stm32f103cb \ calliope-mini \ + cc1350-launchpad \ cc2650-launchpad \ cc2650stk \ + e104-bt5010a-tb \ + e104-bt5011a-tb \ + feather-m0-wifi \ + gd32vf103c-start \ hifive1 \ hifive1b \ i-nucleo-lrwan1 \ @@ -24,16 +30,23 @@ BOARD_INSUFFICIENT_MEMORY := \ nucleo-f042k6 \ nucleo-f070rb \ nucleo-f072rb \ + nucleo-f103rb \ nucleo-f302r8 \ nucleo-f303k8 \ nucleo-f334r8 \ nucleo-l011k4 \ nucleo-l031k6 \ nucleo-l053r8 \ + nucleo-l073rz \ + olimexino-stm32 \ opencm904 \ + openmote-b \ samd10-xmini \ saml10-xpro \ saml11-xpro \ + seeedstudio-gd32 \ + sipeed-longan-nano \ + sipeed-longan-nano-tft \ slstk3400a \ spark-core \ stk3200 \ diff --git a/examples/lwm2m/README.md b/examples/lwm2m/README.md index 8a722ca85fa4..784969196236 100644 --- a/examples/lwm2m/README.md +++ b/examples/lwm2m/README.md @@ -41,12 +41,19 @@ Router, you might want to specify: java -jar ./leshan-server-demo.jar -lh fd00:dead:beef::1 ``` +In the security section click 'Add Security Information', select the security mode +'Pre-Shared Key', and enter the Client endpoint name and the security information +(Identity and Key). + #### Bootstrap server LwM2M provides a bootstrapping mechanism to provide the clients with information to register to one or more servers. To test this mechanism both the previous server and a bootstrap server should be running. Eclipse Leshan also provides a bootstrap server demo. -By default the bootstrap server option is disabled, it can be enabled by defining -`CONFIG_LWM2M_BOOTSTRAP` as 1 (see the Makefile in this application). +By default the security instance created in the application assumes that a standard LwM2M Server is +used. To indicate that the configuration corresponds to a LwM2M Bootstrap Server, set the +corresponding argument (`is_bootstrap`) to true. Also, bootstrap support needs to be enabled in the +wakaama package configurations. This can be done via `make menuconfig` or by setting the CFLAG +`CONFIG_LWM2M_BOOTSTRAP`. To run the bootstrap server, make sure that the ports it uses are different from the ones of previous server (default are 5683 for CoAP, 5684 for CoAPs, @@ -66,34 +73,34 @@ BS_COAPSPORT=5686 BS_WEBPORT=8888 # run the server -java -jar ./leshan-bsserver-demo.jar --coapport ${BS_COAPPORT} \ - --coapsport ${BS_COAPSPORT} --webport ${BS_WEBPORT} +java -jar ./leshan-bsserver-demo.jar --coap-port ${BS_COAPPORT} \ + --coaps-port ${BS_COAPSPORT} --web-port ${BS_WEBPORT} ``` To set up the configuration of the node and the server: 1. Click the `Add new client bootstrap configuration` button. -2. Fill in the name of the device, it **should** match the one set in - `lwm2m.h` as `CONFIG_LWM2M_DEVICE_NAME`. +2. Fill in the name of the device, it **should** match the one set as `CONFIG_LWM2M_DEVICE_NAME`, + in `objects/device.h`. 3. Using the `LWM2M Server` tab enter the address where the LwM2M server is - listening. For now only `No security` mode can be used. + listening. Here you can select `No security` or `Pre-Shared Key` modes. ### Running the client -The address set as `CONFIG_LWM2M_SERVER_URI` (in `lwm2m.h` or via `menuconfig`) -should be reachable from the node, e.g. either running on native with a tap -interface or as a mote connected to a +The server address is set by the application, during the instantiation of the Security object. +It can be set via `menuconfig` or the environmental variable `LWM2M_SERVER_URI`. It should be +reachable from the node, e.g. either running on native with a tap interface or as a mote connected +to a [border router](https://github.com/RIOT-OS/RIOT/tree/master/examples/gnrc_border_router). -Also, if a bootstrap server is being used the macro `CONFIG_LWM2M_BOOTSTRAP` should be -defined as 1. - -The server URI for the example is being defined using the variable `SERVER_URI` -in the Makefile, and can be changed when compiling. +Also, if a bootstrap server is being used, it should be configured in the application via +`menuconfig` or setting the environmental variable `LWM2M_SERVER_BOOTSTRAP` to 1. This information +is used in the Security object instance. #### Configure, compile and run The Wakaama package can be configured via Kconfig. Its options are placed -under `Packages > Configure Wakaama LwM2M`. To access the configuration -interface you can run: +under `Packages > Configure Wakaama LwM2M`. There is also an application-specific configuration +menu. There the Server URI and credentials can be set. To access the configuration interface you +can run: ``` make menuconfig ``` diff --git a/examples/lwm2m/app.config b/examples/lwm2m/app.config new file mode 100644 index 000000000000..3bbf490625ff --- /dev/null +++ b/examples/lwm2m/app.config @@ -0,0 +1,2 @@ +CONFIG_DTLS_PEER_MAX=2 +CONFIG_DTLS_HANDSHAKE_BUFSIZE_EXP=9 diff --git a/examples/lwm2m/credentials.h b/examples/lwm2m/credentials.h new file mode 100644 index 000000000000..c48f5aa544f8 --- /dev/null +++ b/examples/lwm2m/credentials.h @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2021 HAW Hamburg + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief PSK and RPK credentials for the LwM2M example. + * + * @author Leandro Lanzieri + * + * @} + */ + +#ifndef CREDENTIALS_H +#define CREDENTIALS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** + * @brief Default PSK key ID. + */ +#ifndef CONFIG_LWM2M_PSK_ID +#define CONFIG_LWM2M_PSK_ID "Client_Identity" +#endif + +/** + * @brief Default PSK secret. + */ +#ifndef CONFIG_LWM2M_PSK_KEY +#define CONFIG_LWM2M_PSK_KEY "ThisIsRIOT!" +#endif + +static const uint8_t psk_id[] = CONFIG_LWM2M_PSK_ID; +static const uint8_t psk_key[] = CONFIG_LWM2M_PSK_KEY; + +/* openssl ec -in keys.der -inform DER -pubout -outform DER | xxd -i */ +static const uint8_t rpk_pub[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0xa0, 0xc3, 0x8e, 0xcb, 0xa1, + 0x02, 0xeb, 0x5d, 0x25, 0x96, 0x98, 0xbb, 0x60, 0x8e, 0x28, 0x19, 0x56, 0x06, 0x96, 0x70, 0x15, + 0x9b, 0x54, 0xff, 0xd9, 0x60, 0x32, 0xc3, 0x3e, 0x89, 0x08, 0xae, 0x3a, 0x33, 0x2f, 0x54, 0x5f, + 0x68, 0xa2, 0xac, 0xd1, 0xb9, 0xdf, 0x2b, 0x79, 0x65, 0x49, 0x3f, 0x1c, 0xae, 0x64, 0x7a, 0x32, + 0x02, 0xe4, 0x32, 0x8d, 0x6b, 0x22, 0x67, 0x83, 0x0d, 0x7c, 0xb2 +}; + +/* openssl ec -in keys.der -inform DER -no_public -outform DER | xxd -i */ +static const uint8_t rpk_priv[] = { + 0x30, 0x31, 0x02, 0x01, 0x01, 0x04, 0x20, 0xf9, 0x00, 0xb7, 0x31, 0xc4, 0xa7, 0x09, 0xcd, 0x90, + 0x69, 0xc8, 0xac, 0x60, 0xc4, 0x70, 0x58, 0x12, 0xe9, 0xb8, 0x2e, 0x29, 0x12, 0x3c, 0xd1, 0x74, + 0x12, 0xbc, 0xf5, 0x81, 0xe5, 0xb5, 0x04, 0xa0, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, + 0x03, 0x01, 0x07 +}; + +/* provided by server */ +static const uint8_t server_rpk_pub[] = { + 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, + 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03, 0x42, 0x00, 0x04, 0x8b, 0xd5, 0x0f, 0x73, 0xe2, + 0x1d, 0x8f, 0xa3, 0x04, 0x2c, 0x83, 0xd2, 0x1e, 0x85, 0x57, 0x0e, 0xcd, 0xee, 0xf0, 0xc1, 0x14, + 0x9b, 0xeb, 0x05, 0x4f, 0xc9, 0x26, 0x3f, 0xab, 0x6d, 0x43, 0x0b, 0xf8, 0xb9, 0xc9, 0x18, 0x74, + 0x6f, 0xa1, 0x89, 0x71, 0x92, 0xb2, 0x8f, 0x2f, 0x2a, 0xf2, 0xa1, 0xde, 0xed, 0xf2, 0x81, 0x8d, + 0xe4, 0xc2, 0x76, 0xc3, 0x15, 0xff, 0x70, 0xd4, 0xa5, 0x7d, 0x88, +}; + +#ifdef __cplusplus +} +#endif + +#endif /* CREDENTIALS_H */ diff --git a/examples/lwm2m/lwm2m_cli.c b/examples/lwm2m/lwm2m_cli.c index b2030846695a..9b9d63a39ecb 100644 --- a/examples/lwm2m/lwm2m_cli.c +++ b/examples/lwm2m/lwm2m_cli.c @@ -21,8 +21,13 @@ #include "lwm2m_client.h" #include "lwm2m_client_objects.h" #include "lwm2m_platform.h" -#include "objects/light_control.h" + #include "objects/common.h" +#include "objects/device.h" +#include "objects/security.h" +#include "objects/light_control.h" + +#include "credentials.h" #define LED_COLOR "FFFFFF" #define LED_APP_TYPE "LED 0" @@ -58,12 +63,13 @@ void lwm2m_cli_init(void) lwm2m_client_init(&client_data); /* add objects that will be registered */ - obj_list[0] = lwm2m_client_get_security_object(&client_data); - obj_list[1] = lwm2m_client_get_server_object(&client_data); - obj_list[2] = lwm2m_client_get_device_object(&client_data); + obj_list[0] = lwm2m_object_security_init(&client_data); + obj_list[1] = lwm2m_client_get_server_object(&client_data, CONFIG_LWM2M_SERVER_SHORT_ID); + obj_list[2] = lwm2m_object_device_init(&client_data); obj_list[3] = lwm2m_object_light_control_init(&client_data); - lwm2m_obj_light_control_args_t args = { + /* create light control object instance */ + lwm2m_obj_light_control_args_t light_args = { .cb = _light_cb, .cb_arg = NULL, .color = LED_COLOR, @@ -72,12 +78,54 @@ void lwm2m_cli_init(void) .app_type_len = sizeof(LED_APP_TYPE) - 1 }; - int res = lwm2m_object_light_control_instance_create(&args, 0); - + int res = lwm2m_object_light_control_instance_create(&light_args, 0); if (res < 0) { puts("Error instantiating light control"); } + /* create security object instance */ + lwm2m_obj_security_args_t security_args = { + .server_id = CONFIG_LWM2M_SERVER_SHORT_ID, + .server_uri = CONFIG_LWM2M_SERVER_URI, + .security_mode = LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY, + .pub_key_or_id = rpk_pub, + .pub_key_or_id_len = sizeof(rpk_pub), + .secret_key = rpk_priv, + .secret_key_len = sizeof(rpk_priv), + .server_pub_key = server_rpk_pub, + .server_pub_key_len = sizeof(server_rpk_pub), + .is_bootstrap = false, /* set to true when using Bootstrap server */ + .client_hold_off_time = 5, + .bootstrap_account_timeout = 0 + }; + + if (IS_ACTIVE(CONFIG_LWM2M_CRED_PSK)) { + security_args.security_mode = LWM2M_SECURITY_MODE_PRE_SHARED_KEY; + security_args.pub_key_or_id = psk_id; + security_args.pub_key_or_id_len = sizeof(psk_id) - 1; + security_args.secret_key = psk_key; + security_args.secret_key_len = sizeof(psk_key) - 1; + } + else if (IS_ACTIVE(CONFIG_LWM2M_CRED_RPK)) { + security_args.security_mode = LWM2M_SECURITY_MODE_RAW_PUBLIC_KEY; + security_args.pub_key_or_id = rpk_pub; + security_args.pub_key_or_id_len = sizeof(rpk_pub); + security_args.secret_key = rpk_priv; + security_args.secret_key_len = sizeof(rpk_priv); + security_args.server_pub_key = server_rpk_pub; + security_args.server_pub_key_len = sizeof(server_rpk_pub); + } + else { + puts("Error: you must select a credential type"); + return; + } + + res = lwm2m_object_security_instance_create(&security_args, 1); + if (res < 0) { + puts("Could not instantiate the security object"); + return; + } + if (!obj_list[0] || !obj_list[1] || !obj_list[2]) { puts("Could not create mandatory objects"); } @@ -119,7 +167,7 @@ int lwm2m_cli_cmd(int argc, char **argv) if (!strcmp(argv[1], "start")) { /* run the LwM2M client */ - if (!connected && lwm2m_client_run(&client_data, obj_list, OBJ_COUNT)) { + if (!connected && lwm2m_client_run(&client_data, obj_list, ARRAY_SIZE(obj_list))) { connected = 1; } return 0; From dca33c92bd90bd8403c8c9b004aa26bf66665633 Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Fri, 12 Mar 2021 19:07:39 +0100 Subject: [PATCH 7/8] pkg/wakaama: improve documentation --- pkg/wakaama/doc.txt | 112 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 111 insertions(+), 1 deletion(-) diff --git a/pkg/wakaama/doc.txt b/pkg/wakaama/doc.txt index 800a07a68a4c..66410835151b 100644 --- a/pkg/wakaama/doc.txt +++ b/pkg/wakaama/doc.txt @@ -3,7 +3,117 @@ * @ingroup pkg * @ingroup net * @brief LwM2M implementation based on the Wakaama package - * @see https://github.com/eclipse/wakaama + * + * Lightweight Machine to Machine is a device management protocol designed for sensor networks, + * designed for remote management of M2M devices and related service enablement. It defines an + * extensible resource and data model and builds top of CoAP. LwM2M has been specified by the + * OMA SpecWorks Device Management Working Group. The specification is freely available + * [here](http://openmobilealliance.org/wp/index.html). * * For a list of the supported objects see @ref lwm2m_objects. + * The client implementation is based on the Eclipse + * [Wakaama project](https://github.com/eclipse/wakaama) + * + * + * ## Usage + * A LwM2M Client organizes resources as object instances. The LwM2M engine is independent of the + * objects that the client exposes, but it needs at least 3 mandatory ones: + * @ref lwm2m_objects_device "Device object", @ref lwm2m_objects_security "Security object" and a + * Server object. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * #include "lwm2m_client.h" + * #include "lwm2m_client_objects.h" + * #include "objects/security.h" + * #include "objects/device.h" + * #include "net/credman.h" + * + * // hold references to object handles + * lwm2m_object_t *obj_list[3]; + * + * // LwM2M Client instance + * lwm2m_client_data_t client_data; + * + * // short ID for internal reference of the server + * #define CONFIG_LWM2M_SERVER_SHORT_ID 1 + * + * int main(void) + * { + * + * // initiate the LwM2M Client + * lwm2m_client_init(&client_data); + * + * // arguments for instantiating a security object + * lwm2m_obj_security_args_t args = { + * .server_id = CONFIG_LWM2M_SERVER_SHORT_ID, + * .server_uri = "coap://[fd00:dead:beef::1]:5683", + * .security_mode = LWM2M_SECURITY_MODE_NONE, + * .is_bootstrap = false, + * .client_hold_off_time = 5, + * .bootstrap_account_timeout = 0 + * }; + * + * // get the Security object handle + * obj_list[0] = lwm2m_object_security_init(&client_data); + * + * // create a new Security object instance + * int res = lwm2m_object_security_instance_create(&args, 1); + * if (res < 0) { + * puts("Could not instantiate the security object"); + * return; + * } + * + * // get the Server object handle (only single instance for now) + * obj_list[1] = lwm2m_client_get_server_object(&client_data, CONFIG_LWM2M_SERVER_SHORT_ID); + * + * // device object has a single instance. All the information for now is defined at compile-time + * obj_list[2] = lwm2m_object_device_get(); + * + * // run the LwM2M client + * lwm2m_client_run(&client_data, obj_list, ARRAY_SIZE(obj_list); + * } + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The LwM2M Client will connect to the specified LwM2M server and register itself. After that, the + * server will be able to perform Read, Write, Create, Delete, Execute and Observe operations on the + * resources. + * + * ### DTLS support + * With the configuration above plain CoAP is used. To secure the connection with the LwM2M Server, + * a credential is needed in the Security object instance. To enable DTLS support add the module + * `wakaama_client_dtls`. This uses the @ref net_sock_dtls, so you will need to select an + * implementation of it (e.g. `USEPKG += tinydtls`). Currently Pre-Shared Key (PSK) and Raw Public + * Key (RPK) modes are supported. + * + * To see how to use DTLS credentials, go to the usage section of @ref lwm2m_objects_security. + * + * ### Using a LwM2M Bootstrap Server + * A bootstrap server gives a LwM2M deployment more flexibility. Information on how to connect to the + * LwM2M bootstrap server is defined at compile-time in the client (including URI and potentially + * needed credentials). The client connects to the bootstrap server on boot, which installs on the + * node the information needed to connect to the LwM2M servers. + * + * To enable bootstrap support, set the `LWM2M_BOOTSTRAP` option on Kconfig or set the CFLAG + * `CONFIG_LWM2M_BOOTSTRAP=1`. You will also need to specify during the security object instantiation + * that the information corresponds to a bootstrap server. DTLS is also supported for the bootstrap + * server: + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {.c} + * lwm2m_obj_security_args_t args = { + * .server_id = 1, + * .server_uri = "coaps://[fd00:dead:beef::1]:5684", + * .security_mode = LWM2M_SECURITY_MODE_PRE_SHARED_KEY, + * .pub_key_or_id = psk_id, + * .pub_key_or_id_len = sizeof(psk_id) - 1, + * .secret_key = psk_key, + * .secret_key_len = sizeof(psk_key) - 1, + * .is_bootstrap = true, + * .client_hold_off_time = 5, + * .bootstrap_account_timeout = 0 + * }; + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Keep into account, that some Sock DTLS implementations may need some extra configuration to handle + * multiple connections. See the `example/wakaama` Makefile. + * */ From 17a3125091066589678e993d88c540e859ab046a Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Wed, 3 Nov 2021 16:58:34 +0100 Subject: [PATCH 8/8] pkg/tinydtls: allow PSK and ECC at the same time --- pkg/tinydtls/Kconfig | 7 ------- 1 file changed, 7 deletions(-) diff --git a/pkg/tinydtls/Kconfig b/pkg/tinydtls/Kconfig index 70a506d0bde9..29480ad7d782 100644 --- a/pkg/tinydtls/Kconfig +++ b/pkg/tinydtls/Kconfig @@ -7,19 +7,12 @@ menu "tinydtls" depends on USEPKG_TINYDTLS -# TODO change to multiple choice after DTLS application support enabling more than one types of cypher suites -choice - bool "Cipher suite to enable" - default DTLS_PSK - config DTLS_PSK bool "TLS_PSK_WITH_AES_128_CCM_8" config DTLS_ECC bool "TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8" -endchoice - config DTLS_DEBUG bool "Enable debug log" help