diff --git a/examples/dtls-sock/Makefile b/examples/dtls-sock/Makefile new file mode 100644 index 0000000000..973c6edb21 --- /dev/null +++ b/examples/dtls-sock/Makefile @@ -0,0 +1,54 @@ +# name of your application +APPLICATION = dtls_sock + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# TinyDTLS only has support for 32-bit architectures ATM +FEATURES_REQUIRED += arch_32bit + +# Include packages that pull up and auto-init the link layer. +# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present +USEMODULE += gnrc_netdev_default +USEMODULE += auto_init_gnrc_netif +# Specify the mandatory networking modules for IPv6 and UDP +USEMODULE += gnrc_ipv6_default +USEMODULE += gnrc_sock_udp + +# Use tinydtls for sock_dtls +USEMODULE += tinydtls_sock_dtls + +# Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands + +# UDP Port to use (20220 is default for DTLS). +DTLS_PORT ?= 20220 +CFLAGS += -DDTLS_DEFAULT_PORT=$(DTLS_PORT) + +# NOTE: If no cipher suite is selected, DTLS_PSK is used by default. +# This adds support for TLS_PSK_WITH_AES_128_CCM_8 +CFLAGS += -DDTLS_PSK +# This adds support for TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 +# CFLAGS += -DDTLS_ECC + +# Uncomment to enable debug logs +# CFLAGS += -DDTLS_DEBUG +# When DTLS_DEBUG is set and using tinydtls, verbosity of debug log can be set with +# Values: 0:EMERG (Default), 1:ALERT 2:CRIT 3:WARN 4:NOTICE 5:INFO 6:DEBUG +# TINYDTLS_LOG=3 + +# FIXME: This is a temporary patch +CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(2*THREAD_STACKSIZE_LARGE\) + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/dtls-sock/Makefile.ci b/examples/dtls-sock/Makefile.ci new file mode 100644 index 0000000000..facfffe7d3 --- /dev/null +++ b/examples/dtls-sock/Makefile.ci @@ -0,0 +1,41 @@ +BOARD_INSUFFICIENT_MEMORY := \ + airfy-beacon \ + b-l072z-lrwan1 \ + blackpill \ + blackpill-128kib \ + bluepill \ + bluepill-128kib \ + calliope-mini \ + cc1352-launchpad \ + cc2650-launchpad \ + cc2650stk \ + hifive1 \ + hifive1b \ + i-nucleo-lrwan1 \ + lsn50 \ + maple-mini \ + microbit \ + nrf51dongle \ + nrf6310 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f103rb \ + nucleo-f302r8 \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + nucleo-l073rz \ + opencm904 \ + saml10-xpro \ + saml11-xpro \ + spark-core \ + stm32f0discovery \ + stm32f030f4-demo \ + stm32l0538-disco \ + stm32mindev \ + yunjia-nrf51822 + # \ No newline at end of file diff --git a/examples/dtls-sock/README.md b/examples/dtls-sock/README.md new file mode 100644 index 0000000000..59a6ed4411 --- /dev/null +++ b/examples/dtls-sock/README.md @@ -0,0 +1,42 @@ +# DTLS sock example + +This example shows how to use DTLS sock `sock_dtls_t`. + +## Testing using RIOT `native` + +For testing, we can use two RIOT `native` RIOT instances. For that first we +need to prepare the network interfaces: + +```bash +$ ./../../dist/tools/tapsetup/tapsetup --create 2 +``` + +For the server instance: + +``` +$ PORT=tap0 make all term +[...] +> dtlss start +ifconfig +``` + +For the client: + +``` +$ PORT=tap1 make all term +[...] +> dtlsc "DATA to send" +``` + +## Debug logs + +To enable debug logs uncomment `CFLAGS += -DDTLS_DEBUG` in the Makefile. +Tinydtls supports setting the log level. See Makefile for more info. + +## Configs and constraints + +DTLS sock acts as a wrapper for the underlying DTLS stack and as such, the +constraints that applies specifically to the stack are also applied here. +For tinydtls, please refer to [dtls-echo README][1]. + +[1]: https://github.com/RIOT-OS/RIOT/blob/master/examples/dtls-echo/README.md diff --git a/examples/dtls-sock/dtls-client.c b/examples/dtls-sock/dtls-client.c new file mode 100644 index 0000000000..bb53638468 --- /dev/null +++ b/examples/dtls-sock/dtls-client.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2019 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 DTLS sock client example + * + * @author Aiman Ismail + */ + +#include + +#include "net/sock/udp.h" +#include "net/sock/dtls.h" +#include "net/ipv6/addr.h" +#include "net/credman.h" + +#include "tinydtls_keys.h" + +#ifndef DTLS_DEFAULT_PORT +#define DTLS_DEFAULT_PORT 20220 /* DTLS default port */ +#endif + +#define SOCK_DTLS_CLIENT_TAG (2) + +#ifdef DTLS_ECC +static const ecdsa_public_key_t other_pubkeys[] = { + { .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y }, +}; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_ECDSA, + .tag = SOCK_DTLS_CLIENT_TAG, + .params = { + .ecdsa = { + .private_key = ecdsa_priv_key, + .public_key = { + .x = ecdsa_pub_key_x, + .y = ecdsa_pub_key_y, + }, + .client_keys = (ecdsa_public_key_t *)other_pubkeys, + .client_keys_size = ARRAY_SIZE(other_pubkeys), + } + }, +}; + +#else /* ifdef DTLS_PSK */ +static const uint8_t psk_id_0[] = PSK_DEFAULT_IDENTITY; +static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_PSK, + .tag = SOCK_DTLS_CLIENT_TAG, + .params = { + .psk = { + .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, + .id = { .s = psk_id_0, .len = sizeof(psk_id_0) - 1, }, + } + }, +}; +#endif + +static int client_send(char *addr_str, char *data, size_t datalen) +{ + ssize_t res; + sock_udp_t udp_sock; + sock_dtls_t dtls_sock; + sock_dtls_session_t session; + sock_udp_ep_t remote; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + local.port = 12345; + remote.port = DTLS_DEFAULT_PORT; + + /* get interface */ + char* iface = ipv6_addr_split_iface(addr_str); + if (iface) { + int pid = atoi(iface); + if (gnrc_netif_get_by_pid(pid) == NULL) { + puts("Invalid network interface"); + return -1; + } + remote.netif = pid; + } else if (gnrc_netif_numof() == 1) { + /* assign the single interface found in gnrc_netif_numof() */ + remote.netif = gnrc_netif_iter(NULL)->pid; + } else { + /* no interface is given, or given interface is invalid */ + /* FIXME This probably is not valid with multiple interfaces */ + remote.netif = SOCK_ADDR_ANY_NETIF; + } + + if (!ipv6_addr_from_str((ipv6_addr_t *)remote.addr.ipv6, addr_str)) { + puts("Error parsing destination address"); + return -1; + } + + if (sock_udp_create(&udp_sock, &local, NULL, 0) < 0) { + puts("Error creating UDP sock"); + return -1; + } + + if (sock_dtls_create(&dtls_sock, &udp_sock, + SOCK_DTLS_CLIENT_TAG, + SOCK_DTLS_1_2, SOCK_DTLS_CLIENT) < 0) { + puts("Error creating DTLS sock"); + sock_udp_close(&udp_sock); + return -1; + } + + res = credman_add(&credential); + if (res < 0 && res != CREDMAN_EXIST) { + /* ignore duplicate credentials */ + printf("Error cannot add credential to system: %zd\n", res); + return -1; + } + + res = sock_dtls_session_create(&dtls_sock, &remote, &session); + if (res < 0) { + printf("Error creating session: %zd\n", res); + sock_dtls_close(&dtls_sock); + sock_udp_close(&udp_sock); + return -1; + } + + if (sock_dtls_send(&dtls_sock, &session, data, datalen) < 0) { + puts("Error sending data"); + } + else { + printf("Sent DTLS message\n"); + + uint8_t rcv[512]; + if (sock_dtls_recv(&dtls_sock, &session, rcv, sizeof(rcv), SOCK_NO_TIMEOUT) < 0) { + printf("Error receiving DTLS message\n"); + } + else { + printf("Received DTLS message\n"); + } + } + + puts("Terminating"); + sock_dtls_session_destroy(&dtls_sock, &session); + sock_dtls_close(&dtls_sock); + sock_udp_close(&udp_sock); + return 0; +} + +int dtls_client_cmd(int argc, char **argv) +{ + if (argc != 3) { + printf("usage %s \n", argv[0]); + return 1; + } + + return client_send(argv[1], argv[2], strlen(argv[2])); +} diff --git a/examples/dtls-sock/dtls-server.c b/examples/dtls-sock/dtls-server.c new file mode 100644 index 0000000000..693621ae3e --- /dev/null +++ b/examples/dtls-sock/dtls-server.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2019 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 DTLS sock server example + * + * @author Aiman Ismail + */ + +#include + +#include "net/sock/udp.h" +#include "net/sock/dtls.h" +#include "net/credman.h" +#include "msg.h" +#include "thread.h" + +#include "tinydtls_keys.h" + +#ifndef DTLS_DEFAULT_PORT +#define DTLS_DEFAULT_PORT (20220) /* DTLS default port */ +#endif + +#define SOCK_DTLS_SERVER_TAG (10) +#define DTLS_STOP_SERVER_MSG 0x4001 /* Custom IPC type msg. */ +#define READER_QUEUE_SIZE (8U) + +char _dtls_server_stack[THREAD_STACKSIZE_MAIN + + THREAD_EXTRA_STACKSIZE_PRINTF]; + +static kernel_pid_t _dtls_server_pid = KERNEL_PID_UNDEF; + +#ifdef DTLS_ECC +static const ecdsa_public_key_t other_pubkeys[] = { + { .x = ecdsa_pub_key_x, .y = ecdsa_pub_key_y }, +}; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_ECDSA, + .tag = SOCK_DTLS_SERVER_TAG, + .params = { + .ecdsa = { + .private_key = ecdsa_priv_key, + .public_key = { + .x = ecdsa_pub_key_x, + .y = ecdsa_pub_key_y, + }, + .client_keys = (ecdsa_public_key_t *)other_pubkeys, + .client_keys_size = ARRAY_SIZE(other_pubkeys), + }, + }, +}; +#else /* #ifdef DTLS_PSK */ +static const uint8_t psk_key_0[] = PSK_DEFAULT_KEY; + +static const credman_credential_t credential = { + .type = CREDMAN_TYPE_PSK, + .tag = SOCK_DTLS_SERVER_TAG, + .params = { + .psk = { + .key = { .s = psk_key_0, .len = sizeof(psk_key_0) - 1, }, + }, + }, +}; +#endif + +void *dtls_server_wrapper(void *arg) +{ + (void) arg; + + ssize_t res; + bool active = true; + msg_t _reader_queue[READER_QUEUE_SIZE]; + msg_t msg; + uint8_t rcv[512]; + + /* Prepare (thread) messages reception */ + msg_init_queue(_reader_queue, READER_QUEUE_SIZE); + + sock_dtls_session_t session; + sock_dtls_t sock; + sock_udp_t udp_sock; + sock_udp_ep_t local = SOCK_IPV6_EP_ANY; + local.port = DTLS_DEFAULT_PORT; + sock_udp_create(&udp_sock, &local, NULL, 0); + + res = sock_dtls_create(&sock, &udp_sock, SOCK_DTLS_SERVER_TAG, + SOCK_DTLS_1_2, SOCK_DTLS_SERVER); + if (res < 0) { + puts("Error creating DTLS sock"); + return NULL; + } + + res = credman_add(&credential); + if (res < 0 && res != CREDMAN_EXIST) { + /* ignore duplicate credentials */ + printf("Error cannot add credential to system: %zd\n", res); + return NULL; + } + + while (active) { + msg_try_receive(&msg); + if (msg.type == DTLS_STOP_SERVER_MSG) { + active = false; + } + else { + res = sock_dtls_recv(&sock, &session, rcv, sizeof(rcv), + 10 * US_PER_SEC); + if (res < 0) { + if (res != -ETIMEDOUT) { + printf("Error receiving UDP over DTLS %zd", res); + } + continue; + } + printf("Received %zd bytes -- (echo!)\n", res); + res = sock_dtls_send(&sock, &session, rcv, (size_t)res); + if (res < 0) { + printf("Error resending DTLS message: %zd", res); + } + } + } + + sock_dtls_session_destroy(&sock, &session); + sock_dtls_close(&sock); + sock_udp_close(&udp_sock); + puts("Terminating"); + msg_reply(&msg, &msg); /* Basic answer to the main thread */ + return NULL; +} + +static void start_server(void) +{ + /* Only one instance of the server */ + if (_dtls_server_pid != KERNEL_PID_UNDEF) { + puts("Error: server already running"); + return; + } + + /* Start the server thread */ + _dtls_server_pid = thread_create(_dtls_server_stack, + sizeof(_dtls_server_stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + dtls_server_wrapper, NULL, "dtls_server"); + + /* Uncommon but better be sure */ + if (_dtls_server_pid < 0) { + printf("ERROR: failed to create thread: %d\n", _dtls_server_pid); + _dtls_server_pid = KERNEL_PID_UNDEF; + } +} + +static void stop_server(void) +{ + /* check if server is running at all */ + if (_dtls_server_pid == KERNEL_PID_UNDEF) { + puts("Error: DTLS server is not running"); + return; + } + + /* prepare the stop message */ + msg_t m; + m.type = DTLS_STOP_SERVER_MSG; + + puts("Stopping server..."); + + /* send the stop message to thread AND wait for (any) answer */ + msg_send_receive(&m, &m, _dtls_server_pid); + + _dtls_server_pid = KERNEL_PID_UNDEF; + puts("Success: DTLS server stopped"); +} + +int dtls_server_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s start | stop\n", argv[0]); + return 1; + } + if (strcmp(argv[1], "start") == 0) { + start_server(); + } + else if (strcmp(argv[1], "stop") == 0) { + stop_server(); + } + else { + printf("Error: invalid command. Usage: %s start | stop\n", argv[0]); + return 1; + } + return 0; +} diff --git a/examples/dtls-sock/main.c b/examples/dtls-sock/main.c new file mode 100644 index 0000000000..f80b775466 --- /dev/null +++ b/examples/dtls-sock/main.c @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2019 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 Example application for DTLS sock + * + * @author Aiman Ismail + */ + +#include + +#include "shell.h" + +extern int dtls_client_cmd(int argc, char **argv); +extern int dtls_server_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "dtlsc", "Start a DTLS client", dtls_client_cmd }, + { "dtlss", "Start and stop a DTLS server", dtls_server_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + puts("DTLS sock example application"); + + /* start shell */ + puts("All up, running the shell now"); + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + + /* should never be reached */ + return 0; +} diff --git a/examples/dtls-sock/tinydtls_keys.h b/examples/dtls-sock/tinydtls_keys.h new file mode 100644 index 0000000000..27f1ee3882 --- /dev/null +++ b/examples/dtls-sock/tinydtls_keys.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 Inria + * + * 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 keys for the dtls-sock example. + * + * @author Raul Fuentes + * + * @} + */ + +#ifndef TINYDTLS_KEYS_H +#define TINYDTLS_KEYS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Default keys examples for tinyDTLS (for RIOT, Linux and Contiki) + */ +#ifdef DTLS_PSK +#define PSK_DEFAULT_IDENTITY "Client_identity" +#define PSK_DEFAULT_KEY "secretPSK" +#define PSK_OPTIONS "i:k:" +#define PSK_ID_MAXLEN 32 +#define PSK_MAXLEN 32 + +#endif /* DTLS_PSK */ + +#ifdef DTLS_ECC +static const unsigned char ecdsa_priv_key[] = { + 0x41, 0xC1, 0xCB, 0x6B, 0x51, 0x24, 0x7A, 0x14, + 0x43, 0x21, 0x43, 0x5B, 0x7A, 0x80, 0xE7, 0x14, + 0x89, 0x6A, 0x33, 0xBB, 0xAD, 0x72, 0x94, 0xCA, + 0x40, 0x14, 0x55, 0xA1, 0x94, 0xA9, 0x49, 0xFA +}; + +static const unsigned char ecdsa_pub_key_x[] = { + 0x36, 0xDF, 0xE2, 0xC6, 0xF9, 0xF2, 0xED, 0x29, + 0xDA, 0x0A, 0x9A, 0x8F, 0x62, 0x68, 0x4E, 0x91, + 0x63, 0x75, 0xBA, 0x10, 0x30, 0x0C, 0x28, 0xC5, + 0xE4, 0x7C, 0xFB, 0xF2, 0x5F, 0xA5, 0x8F, 0x52 +}; + +static const unsigned char ecdsa_pub_key_y[] = { + 0x71, 0xA0, 0xD4, 0xFC, 0xDE, 0x1A, 0xB8, 0x78, + 0x5A, 0x3C, 0x78, 0x69, 0x35, 0xA7, 0xCF, 0xAB, + 0xE9, 0x3F, 0x98, 0x72, 0x09, 0xDA, 0xED, 0x0B, + 0x4F, 0xAB, 0xC3, 0x6F, 0xC7, 0x72, 0xF8, 0x29 +}; +#endif /* DTLS_ECC */ +#ifdef __cplusplus +} +#endif + +#endif /* TINYDTLS_KEYS_H */ diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 492068a06e..bf1ddd4e84 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -80,6 +80,7 @@ PSEUDOMODULES += sched_cb PSEUDOMODULES += semtech_loramac_rx PSEUDOMODULES += sock PSEUDOMODULES += sock_async +PSEUDOMODULES += sock_dtls PSEUDOMODULES += sock_ip PSEUDOMODULES += sock_tcp PSEUDOMODULES += sock_udp diff --git a/pkg/tinydtls/Makefile.include b/pkg/tinydtls/Makefile.include index d43b9c99c7..a2acb76350 100644 --- a/pkg/tinydtls/Makefile.include +++ b/pkg/tinydtls/Makefile.include @@ -6,6 +6,9 @@ ifeq ($(TOOLCHAIN), llvm) CFLAGS += -Wno-gnu-zero-variadic-macro-arguments -Wno-unused-function endif +INCLUDES += -I$(RIOTBASE)/pkg/tinydtls/include +DIRS += $(RIOTBASE)/pkg/tinydtls/contrib + ifneq (,$(filter tinydtls,$(USEMODULE))) INCLUDES += -I$(PKG_BUILDDIR) # Mandatory for tinyDTLS @@ -27,6 +30,13 @@ ifneq (,$(filter tinydtls,$(USEMODULE))) endif endif + # Enable debug when using tinydtls_sock_dtls + ifneq (,$(filter -DDTLS_DEBUG,$(CFLAGS))) + ifeq (,$(TINYDTLS_LOG)) + CFLAGS += -DTINYDTLS_DEBUG + endif + endif + # Handles the verbosity of tinyDTLS. Default: Minimum or just error messages. ifeq (,$(filter -DTINYDTLS_DEBUG,$(CFLAGS))) ifeq ( , $(TINYDTLS_LOG)) diff --git a/pkg/tinydtls/contrib/Makefile b/pkg/tinydtls/contrib/Makefile new file mode 100644 index 0000000000..d8eb4547b4 --- /dev/null +++ b/pkg/tinydtls/contrib/Makefile @@ -0,0 +1,3 @@ +MODULE := tinydtls_sock_dtls + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/tinydtls/contrib/sock_dtls.c b/pkg/tinydtls/contrib/sock_dtls.c new file mode 100644 index 0000000000..09ccaee00b --- /dev/null +++ b/pkg/tinydtls/contrib/sock_dtls.c @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2019 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. + */ + +/** + * @{ + * + * @file + * @brief tinydtls implementation of @ref net_sock_dtls + * + * @author Aiman Ismail + * @author Leandro Lanzieri + */ + +#include "dtls.h" +#include "net/sock/dtls.h" +#include "net/credman.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" +#include "dtls_debug.h" + +#define DTLS_EVENT_READ (0x01E0) +#define DTLS_EVENT_TIMEOUT (0x01E1) + +#define DTLS_HANDSHAKE_BUFSIZE (256) /**< Size buffer used in handshake + to hold credentials */ +/* ECC handshake takes more time */ +#ifdef DTLS_ECC +#define DTLS_HANDSHAKE_TIMEOUT (30 * US_PER_SEC) +#else +#define DTLS_HANDSHAKE_TIMEOUT (1 * US_PER_SEC) +#endif /* DTLS_ECC */ + +static void _timeout_callback(void *arg); + +#ifdef DTLS_PSK +static int _get_psk_info(struct dtls_context_t *ctx, const session_t *session, + dtls_credentials_type_t type, + const unsigned char *id, size_t id_len, + unsigned char *result, size_t result_length); +#endif /* DTLS_PSK */ + +#ifdef DTLS_ECC +static int _get_ecdsa_key(struct dtls_context_t *ctx, const session_t *session, + const dtls_ecdsa_key_t **result); + +static int _verify_ecdsa_key(struct dtls_context_t *ctx, + const session_t *session, + const unsigned char *other_pub_x, + const unsigned char *other_pub_y, + size_t key_size); +#endif /* DTLS_ECC */ + +static int _write(struct dtls_context_t *ctx, session_t *session, uint8_t *buf, + size_t len); + +static int _read(struct dtls_context_t *ctx, session_t *session, uint8_t *buf, + size_t len); +static int _event(struct dtls_context_t *ctx, session_t *session, + dtls_alert_level_t level, unsigned short code); + +static void _session_to_ep(const session_t *session, sock_udp_ep_t *ep); +static void _ep_to_session(const sock_udp_ep_t *ep, session_t *session); + +static dtls_handler_t _dtls_handler = { + .event = _event, + .write = _write, + .read = _read, +#ifdef DTLS_PSK + .get_psk_info = _get_psk_info, +#endif /* DTLS_PSK */ +#ifdef DTLS_ECC + .get_ecdsa_key = _get_ecdsa_key, + .verify_ecdsa_key = _verify_ecdsa_key, +#endif /* DTLS_ECC */ +}; + +static int _read(struct dtls_context_t *ctx, session_t *session, uint8_t *buf, + size_t len) +{ + (void)session; + msg_t msg = { .type = DTLS_EVENT_READ }; + sock_dtls_t *sock = dtls_get_app_data(ctx); + + DEBUG("sock_dtls: decrypted message arrived\n"); + if (sock->buflen < len && sock->buf) { + DEBUG("sock_dtls: not enough place on buffer for decrypted message\n"); + msg.content.value = -ENOBUFS; + } + else { + memmove(sock->buf, buf, len); + msg.content.value = len; + } + mbox_put(&sock->mbox, &msg); + return len; +} + +static int _write(struct dtls_context_t *ctx, session_t *session, uint8_t *buf, + size_t len) +{ + sock_dtls_t *sock = (sock_dtls_t *)dtls_get_app_data(ctx); + sock_udp_ep_t remote; + + _session_to_ep(session, &remote); + remote.family = AF_INET6; + + ssize_t res = sock_udp_send(sock->udp_sock, buf, len, &remote); + if (res < 0) { + DEBUG("sock_dtls: failed to send DTLS record: %zd\n", res); + } + return res; +} + +static int _event(struct dtls_context_t *ctx, session_t *session, + dtls_alert_level_t level, unsigned short code) +{ + (void)level; + (void)session; + + sock_dtls_t *sock = dtls_get_app_data(ctx); + msg_t msg = { .type = code }; +#ifdef ENABLE_DEBUG + switch(code) { + case DTLS_EVENT_CONNECT: + DEBUG("sock_dtls: event connect\n"); + break; + case DTLS_EVENT_CONNECTED: + DEBUG("sock_dtls: event connected\n"); + break; + case DTLS_EVENT_RENEGOTIATE: + DEBUG("sock_dtls: event renegotiate\n"); + break; + } +#endif /* ENABLE_DEBUG */ + mbox_put(&sock->mbox, &msg); + return 0; +} + +#ifdef DTLS_PSK +static int _get_psk_info(struct dtls_context_t *ctx, const session_t *session, + dtls_credentials_type_t type, + const unsigned char *desc, size_t desc_len, + unsigned char *result, size_t result_length) +{ + (void)ctx; + (void)desc; + (void)desc_len; + int ret; + sock_dtls_session_t _session; + sock_udp_ep_t ep; + sock_dtls_t *sock = (sock_dtls_t *)dtls_get_app_data(ctx); + + _session_to_ep(session, &ep); + memcpy(&_session.ep, &ep, sizeof(sock_udp_ep_t)); + memcpy(&_session.dtls_session, session, sizeof(session_t)); + + credman_credential_t credential; + ret = credman_get(&credential, sock->tag, CREDMAN_TYPE_PSK); + if (ret < 0) { + DEBUG("sock_dtls: no matching PSK credential found\n"); + return dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR); + } + + const void *c = NULL; + size_t c_len = 0; + switch(type) { + case DTLS_PSK_HINT: + DEBUG("sock_dtls: psk hint request\n"); + /* Ignored. See https://tools.ietf.org/html/rfc4279#section-5.2 */ + return 0; + case DTLS_PSK_IDENTITY: + DEBUG("sock_dtls: psk id request\n"); + c = credential.params.psk.id.s; + c_len = credential.params.psk.id.len; + break; + case DTLS_PSK_KEY: + DEBUG("sock_dtls: psk key request\n"); + c = credential.params.psk.key.s; + c_len = credential.params.psk.key.len; + break; + default: + DEBUG("sock:dtls unsupported request type: %d\n", type); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + if (c_len > result_length) { + DEBUG("sock_dtls: not enough memory for credential type: %d\n", type); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + if (c == NULL || c_len == 0) { + DEBUG("sock_dtls: invalid credential params for type %d\n", type); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + memcpy(result, c, c_len); + return c_len; +} +#endif /* DTLS_PSK */ + +#ifdef DTLS_ECC +static int _get_ecdsa_key(struct dtls_context_t *ctx, const session_t *session, + const dtls_ecdsa_key_t **result) +{ + (void)session; + int ret; + sock_dtls_t *sock = (sock_dtls_t *)dtls_get_app_data(ctx); + + credman_credential_t credential; + ret = credman_get(&credential, sock->tag, CREDMAN_TYPE_ECDSA); + if (ret < 0) { + DEBUG("sock_dtls: no matching ecdsa credential found\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + static dtls_ecdsa_key_t key; + key.curve = DTLS_ECDH_CURVE_SECP256R1; + key.priv_key = credential.params.ecdsa.private_key; + key.pub_key_x = credential.params.ecdsa.public_key.x; + key.pub_key_y = credential.params.ecdsa.public_key.y; + *result = &key; + return 0; +} + +static int _verify_ecdsa_key(struct dtls_context_t *ctx, + const session_t *session, + const unsigned char *other_pub_x, + const unsigned char *other_pub_y, size_t key_size) +{ + (void) ctx; + (void) session; + (void) other_pub_y; + (void) other_pub_x; + (void) key_size; + + return 0; +} +#endif /* DTLS_ECC */ + +int sock_dtls_create(sock_dtls_t *sock, sock_udp_t *udp_sock, + credman_tag_t tag, unsigned version, unsigned role) +{ + assert(sock); + assert(udp_sock); + + if (role != SOCK_DTLS_CLIENT && role != SOCK_DTLS_SERVER) { + DEBUG("sock_dtls: invalid role\n"); + return -1; + } + + /* check if tinydtls compiled with wanted DTLS version */ + if (version < SOCK_DTLS_1_0 || version > SOCK_DTLS_1_3) { + DEBUG("sock_dtls: invalid version\n"); + return -1; + } + else if ((version == SOCK_DTLS_1_2) && + (DTLS_VERSION != 0xfefd)) { + DEBUG("sock_dtls: tinydtls not compiled with support for DTLS 1.2\n"); + return -1; + } + else if (version == SOCK_DTLS_1_0 || version == SOCK_DTLS_1_3) { + DEBUG("sock_dtls: tinydtls only support DTLS 1.2\n"); + return -1; + } + + sock->udp_sock = udp_sock; + sock->role = role; + sock->tag = tag; + sock->dtls_ctx = dtls_new_context(sock); + if (!sock->dtls_ctx) { + DEBUG("sock_dtls: error getting DTLS context\n"); + return -1; + } + mbox_init(&sock->mbox, sock->mbox_queue, SOCK_DTLS_MBOX_SIZE); + dtls_set_handler(sock->dtls_ctx, &_dtls_handler); + return 0; +} + +int sock_dtls_session_create(sock_dtls_t *sock, const sock_udp_ep_t *ep, + sock_dtls_session_t *remote) +{ + uint8_t rcv_buffer[DTLS_HANDSHAKE_BUFSIZE]; + msg_t msg; + ssize_t res; + + assert(sock); + assert(ep); + assert(remote); + + /* prepare a the remote party to connect to */ + memcpy(&remote->ep, ep, sizeof(sock_udp_ep_t)); + memcpy(&remote->dtls_session.addr, &ep->addr.ipv6, sizeof(ipv6_addr_t)); + _ep_to_session(ep, &remote->dtls_session); + + /* start a handshake */ + DEBUG("sock_dtls: starting handshake\n"); + res = dtls_connect(sock->dtls_ctx, &remote->dtls_session); + if (res < 0) { + DEBUG("sock_dtls: error establishing a session: %zd\n", res); + return -ENOMEM; + } + else if (res == 0) { + DEBUG("sock_dtls: session already exist. Skip establishing session\n"); + return 0; + } + + /* receive all handshake messages or timeout if timer expires */ + while (!mbox_try_get(&sock->mbox, &msg) || + msg.type != DTLS_EVENT_CONNECTED) { + res = sock_udp_recv(sock->udp_sock, rcv_buffer, sizeof(rcv_buffer), + DTLS_HANDSHAKE_TIMEOUT, &remote->ep); + if (res <= 0) { + DEBUG("sock_dtls: error receiving handshake messages: %zd\n", res); + /* deletes peer created in dtls_connect() */ + dtls_peer_t *peer = dtls_get_peer(sock->dtls_ctx, + &remote->dtls_session); + dtls_reset_peer(sock->dtls_ctx, peer); + return -ETIMEDOUT; + } + + res = dtls_handle_message(sock->dtls_ctx, &remote->dtls_session, + rcv_buffer, res); + /* stop handshake if received fatal level alert */ + if (res == -1) { + return res; + } + } + return 0; +} + +void sock_dtls_session_destroy(sock_dtls_t *sock, sock_dtls_session_t *remote) +{ + dtls_close(sock->dtls_ctx, &remote->dtls_session); +} + +ssize_t sock_dtls_send(sock_dtls_t *sock, sock_dtls_session_t *remote, + const void *data, size_t len) +{ + assert(sock); + assert(remote); + assert(data); + + /* check if session exists, if not create session first then send */ + if (!dtls_get_peer(sock->dtls_ctx, &remote->dtls_session)) { + int res; + + /* no session with remote, creating new session. + * This will also create new peer for this session */ + res = dtls_connect(sock->dtls_ctx, &remote->dtls_session); + if (res < 0) { + DEBUG("sock_dtls: error initiating handshake\n"); + return -ENOMEM; + } + else if (res > 0) { + /* handshake initiated, wait until connected or timed out */ + xtimer_t timeout_timer; + timeout_timer.callback = _timeout_callback; + timeout_timer.arg = sock; + xtimer_set(&timeout_timer, DTLS_HANDSHAKE_TIMEOUT); + + msg_t msg; + do { + mbox_get(&sock->mbox, &msg); + } while ((msg.type != DTLS_EVENT_CONNECTED) && + (msg.type != DTLS_EVENT_TIMEOUT)); + + if (msg.type == DTLS_EVENT_TIMEOUT) { + DEBUG("sock_dtls: handshake process timed out\n"); + + /* deletes peer created in dtls_connect() before */ + dtls_peer_t *peer = dtls_get_peer(sock->dtls_ctx, &remote->dtls_session); + dtls_reset_peer(sock->dtls_ctx, peer); + return -EHOSTUNREACH; + } + xtimer_remove(&timeout_timer); + } + } + + return dtls_write(sock->dtls_ctx, &remote->dtls_session, (uint8_t *)data, + len); +} + +ssize_t sock_dtls_recv(sock_dtls_t *sock, sock_dtls_session_t *remote, + void *data, size_t max_len, uint32_t timeout) +{ + xtimer_t timeout_timer; + + assert(sock); + assert(data); + assert(remote); + + if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) { + timeout_timer.callback = _timeout_callback; + timeout_timer.arg = sock; + xtimer_set(&timeout_timer, timeout); + } + + /* save location to result buffer */ + sock->buf = data; + sock->buflen = max_len; + + /* loop breaks when timeout or application data read */ + while(1) { + uint32_t start_recv = xtimer_now_usec(); + ssize_t res = sock_udp_recv(sock->udp_sock, data, max_len, timeout, + &remote->ep); + if (res <= 0) { + DEBUG("sock_dtls: error receiving UDP packet: %zd\n", res); + xtimer_remove(&timeout_timer); + return res; + } + + if ((timeout != SOCK_NO_TIMEOUT) && (timeout != 0)) { + uint32_t time_passed = (xtimer_now_usec() - start_recv); + timeout = (time_passed > timeout) ? 0: timeout - time_passed; + } + + _ep_to_session(&remote->ep, &remote->dtls_session); + res = dtls_handle_message(sock->dtls_ctx, &remote->dtls_session, + (uint8_t *)data, res); + + /* reset msg type */ + msg_t msg; + if (mbox_try_get(&sock->mbox, &msg)) { + switch(msg.type) { + case DTLS_EVENT_READ: + xtimer_remove(&timeout_timer); + return msg.content.value; + case DTLS_EVENT_TIMEOUT: + DEBUG("sock_dtls: timed out while decrypting message\n"); + return -ETIMEDOUT; + default: + break; + } + } + else if (timeout == 0) { + return -ETIMEDOUT; + } + } + +} + +void sock_dtls_close(sock_dtls_t *sock) +{ + dtls_free_context(sock->dtls_ctx); +} + +void sock_dtls_init(void) +{ + dtls_init(); + dtls_set_log_level(TINYDTLS_LOG_LVL); +} + +static void _ep_to_session(const sock_udp_ep_t *ep, session_t *session) +{ + session->port = ep->port; + session->size = sizeof(ipv6_addr_t) + /* addr */ + sizeof(unsigned short); /* port */ + session->ifindex = ep->netif; + memcpy(&session->addr, &ep->addr.ipv6, sizeof(ipv6_addr_t)); +} + +static void _session_to_ep(const session_t *session, sock_udp_ep_t *ep) +{ + ep->port = session->port; + ep->netif = session->ifindex; + memcpy(&ep->addr.ipv6, &session->addr, sizeof(ipv6_addr_t)); +} + +static void _timeout_callback(void *arg) +{ + msg_t timeout_msg = { .type = DTLS_EVENT_TIMEOUT }; + sock_dtls_t *sock = arg; + mbox_try_put(&sock->mbox, &timeout_msg); +} + +/** @} */ diff --git a/pkg/tinydtls/include/sock_dtls_types.h b/pkg/tinydtls/include/sock_dtls_types.h new file mode 100644 index 0000000000..653b75c3c8 --- /dev/null +++ b/pkg/tinydtls/include/sock_dtls_types.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2019 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. + */ + +/** + * @{ + * + * @file + * @brief tinydtls-specific types and functions definitions + * + * @author Aiman Ismail + * @author Leandro Lanzieri + */ + +#ifndef SOCK_DTLS_TYPES_H +#define SOCK_DTLS_TYPES_H + +#include "dtls.h" +#include "net/sock/udp.h" +#include "net/credman.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef SOCK_DTLS_MBOX_SIZE +#define SOCK_DTLS_MBOX_SIZE (4) /**< Size of DTLS sock mailbox */ +#endif + +/** + * @brief Information about DTLS sock + */ +struct sock_dtls { + dtls_context_t *dtls_ctx; /**< TinyDTLS context for sock */ + sock_udp_t *udp_sock; /**< Underlying UDP sock to use */ + mbox_t mbox; /**< Mailbox for internal event + handling */ + msg_t mbox_queue[SOCK_DTLS_MBOX_SIZE]; /**< Queue for struct + sock_dtls::mbox */ + uint8_t *buf; /**< Buffer to pass decrypted data + back to user */ + size_t buflen; /**< Size of buffer */ + credman_tag_t tag; /**< Credential tag of a registered + (D)TLS credential */ + dtls_peer_type role; /**< DTLS role of the socket */ +}; + +/** + * @brief Information about remote client connected to the server + */ +struct sock_dtls_session { + sock_udp_ep_t ep; /**< Remote endpoint the session + is connected to */ + session_t dtls_session; /**< TinyDTLS session */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* SOCK_DTLS_TYPES_H */ +/** @} */