From 4eb3ef4b4f3bb9cabd61a9d5dbc7c4546103b52c Mon Sep 17 00:00:00 2001 From: Leandro Lanzieri Date: Thu, 11 Mar 2021 15:12:27 +0100 Subject: [PATCH] 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 4f7b0ca917..4d265b095f 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 b2e7014724..e10f5bf102 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 aced1c64cc..e4cd489cf0 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 dbd167c552..416a0c342a 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 0000000000..bc0a161f92 --- /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 0000000000..b4765130b0 --- /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 53dc76704c..800a07a68a 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 6e92ac040e..266f58be2f 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 0cd28d03d4..174eb44556 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 0000000000..87fa0b15a5 --- /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 6cc649fc29..c2b7e38c30 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 \ #