diff --git a/examples/dtls-echo/Makefile b/examples/dtls-echo/Makefile new file mode 100644 index 000000000000..f551e46611ea --- /dev/null +++ b/examples/dtls-echo/Makefile @@ -0,0 +1,73 @@ +# name of your application +APPLICATION = dtls_echo + +# 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)/../.. + +BOARD_BLACKLIST := z1 wsn430-v1_4 wsn430-v1_3b waspmote-pro arduino-mega2560 \ + msb-430h msb-430 chronos telosb msbiot cc2538dk \ + saml21-xpro samr21-xpro arduino-duemilanove arduino-uno + +BOARD_INSUFFICIENT_MEMORY := airfy-beacon chronos msb-430 msb-430h nrf51dongle \ + nrf6310 nucleo-f103 nucleo-f334 pca10000 pca10005 spark-core \ + stm32f0discovery telosb weio wsn430-v1_3b wsn430-v1_4 \ + yunjia-nrf51822 z1 nucleo-f072 cc2650stk nucleo-f030\ + nucleo-f070 + +# 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 sUDP +USEMODULE += gnrc_ipv6_router_default +USEMODULE += gnrc_udp +# Add a routing protocol +USEMODULE += gnrc_rpl +# This application dumps received packets to STDIO using the pktdump module +USEMODULE += gnrc_pktdump +# Additional networking modules that can be dropped if not needed +USEMODULE += gnrc_icmpv6_echo +# Add also the shell, some shell commands +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps + +#TinyDTLs (crypto.c) made use of pthread + ifneq ($(BOARD),native) + USEMODULE += pthread + endif + +# 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: +CFLAGS += -DDEVELHELP + +# NOTE: Add the package for TinyDTLS +USEPKG += tinydtls + +# NOTE: Those are taken from TinyDTLS. As the original Makefiles are +# overwitten is a good idea to preserve them here. +CFLAGS += -DDTLSv12 -DWITH_SHA256 + +# NOTE: This adds support for TLS_PSK_WITH_AES_128_CCM_8 +CFLAGS += -DDTLS_PSK + +# NOTE: This adds support for TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 +CFLAGS += -DDTLS_ECC + +# NOTE: If enabled TinyDTLS' log are disabled (if memory is a issue). +# WARNING: Sometimes the log leads to Stack pointer corrupted. +# The reason is not identified yet. +# If said issue appears, enable this line. +#CFLAGS += -DNDEBUG + +# NOTE: The configuration for socket or non-socket communication in TinyDTLS. +CFLAGS += -DWITH_RIOT_GNRC + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +include $(RIOTBASE)/Makefile.include diff --git a/examples/dtls-echo/README.md b/examples/dtls-echo/README.md new file mode 100644 index 000000000000..4a5a31507f1a --- /dev/null +++ b/examples/dtls-echo/README.md @@ -0,0 +1,77 @@ +# dtls_echo + +This example shows you how to use TinyDTLS with the non-socket approach. + +This code is based on ../gnrc_networking and ../gnrc_tftp. +Is a good idea to read their README.md's for any doubt of how making the +testings. + +## SOCKET vs. Non-socket (GNRC) + +This example is configured to use the GNRC instead of sockets (over GNRC). +At the moment, the configuration must be done manually in the Makefile of +this project. + +## Fast configuration (Between RIOT instances): + +Preparing the logical interfaces: + + ./../../dist/tools/tapsetup/tapsetup --create 2 + +For the server instance: + + make all; PORT=tap1 make term + dtlss start + ifconfig + +Do not forget to copy the IPv6 addresses! + +For the client: + + PORT=tap0 make term + dtlsc "DATA TO DATA TO DATA!" + +# Testings +## Boards + +Those boards that do not support the `../gnrc_networking` example are included +in the `BOARD_INSUFFICIENT_MEMORY`, plus the board `cc2650stk`. + +There are certain boards that are having issues with `crypto.c` and +`dtls_time.h` Which for now are in the the `BOARD_BLACKLIST`. + +The boards that requires `periph_conf.h` are not tested. + +Boards with problem type 1 (`crypto.c`): + z1 + wsn430-v1_4 + wsn430-v1_3b + waspmote-pro + msb-430h + msb-430 + chronos + arduino-mega2560 + +Boards with problem type 2 (`dtls_time.h`): + cc2538dk + msbiot + telosb + +Boards with problem type 3 (Redifinition): + saml21-xpro + samr21-xpro + arduino-uno + arduino-duemilanove + +NOTE: Those on type 1 can be benefit of the following PR: +https://github.com/RIOT-OS/RIOT/issues/2360 +However, there are still issues to fix. + +NOTE: Those on type 2 can be fixed with the patch at +https://github.com/RIOT-OS/RIOT/pull/5974 + +## FIT-LAB + +The code has been tested in the FIT-LAB with M3 motes. +However, erros can occurrs. Enabling the line `CFLAGS += -DNDEBUG` in +the `Makefile` reduces the risk. diff --git a/examples/dtls-echo/dtls-client.c b/examples/dtls-echo/dtls-client.c new file mode 100644 index 000000000000..bef48c3646ec --- /dev/null +++ b/examples/dtls-echo/dtls-client.c @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 The cliend side of TinyDTLS (Simple echo) + * + * @author Raul A. Fuentes Samaniego + * @author Olaf Bergmann + * @author Hauke Mehrtens + * @author Oliver Hahm + * @} + */ + +#include +#include + +#include "net/gnrc.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/udp.h" +#include "net/gnrc/pktdump.h" +#include "timex.h" +#include "xtimer.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +/* TinyDTLS */ +#include "tinydtls.h" +#include "dtls_debug.h" +#include "dtls.h" +#include "global.h" + + + +/* TODO: Remove the UNUSED_PARAM from TinyDTLS' stack? */ +#ifdef __GNUC__ +#define UNUSED_PARAM __attribute__((unused)) +#else +#define UNUSED_PARAM +#endif /* __GNUC__ */ + +#ifdef DTLS_PSK + +#define PSK_DEFAULT_IDENTITY "Client_identity" +#define PSK_DEFAULT_KEY "secretPSK" +#define PSK_OPTIONS "i:k:" + +/* Max size for PSK lowered for embedded devices */ +#define PSK_ID_MAXLEN 32 +#define PSK_MAXLEN 32 + +#endif /* DTLS_PSK */ + +//#define DEFAULT_PORT 20220 /* DTLS default port */ +#define DEFAULT_PORT 61618 /* First valid FEBx address */ + +#define CLIENT_PORT DEFAULT_PORT + 1 +#define MAX_TIMES_TRY_TO_SEND 10 + +static dtls_context_t *dtls_context = NULL; +static char *client_payload; +static size_t buflen = 0; + +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 +}; + + +/** + * @brief This care about getting messages and continue with the DTLS flights. + * This will handle all the packets arriving to the node. + * Will determine if its from a new DTLS peer or a previous one. + */ +static void dtls_handle_read(dtls_context_t *ctx, gnrc_pktsnip_t *pkt) +{ + + static session_t session; + + /* + * NOTE: GNRC (Non-socket) issue: we need to modify the current + * DTLS Context for the IPv6 src (and in a future the port src). + */ + + /* Taken from the tftp server example */ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + gnrc_pktsnip_t *tmp2; + + tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + ipv6_hdr_t *hdr = (ipv6_hdr_t *)tmp2->data; + + ipv6_addr_to_str(addr_str, &hdr->src, sizeof(addr_str)); + + /* + *TODO: More testings with TinyDTLS is neccesary, but seem this is safe. + */ + tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); + udp_hdr_t *udp = (udp_hdr_t *)tmp2->data; + + session.size = sizeof(ipv6_addr_t) + sizeof(unsigned short); + session.port = byteorder_ntohs(udp->src_port); + + DEBUG("DBG-Client: Msg received from \n\t Addr Src: %s" + "\n\t Current Peer: %s \n", addr_str, (char *)dtls_get_app_data(ctx)); + + ipv6_addr_from_str(&session.addr, addr_str); + + dtls_handle_message(ctx, &session, pkt->data, (unsigned int)pkt->size); + +} + +#ifdef DTLS_PSK +static unsigned char psk_id[PSK_ID_MAXLEN] = PSK_DEFAULT_IDENTITY; +static size_t psk_id_length = sizeof(PSK_DEFAULT_IDENTITY) - 1; +static unsigned char psk_key[PSK_MAXLEN] = PSK_DEFAULT_KEY; +static size_t psk_key_length = sizeof(PSK_DEFAULT_KEY) - 1; + +/** + * This function is the "key store" for tinyDTLS. It is called to + * retrieve a key for the given identity within this particular + * session. + */ +static int peer_get_psk_info(struct dtls_context_t *ctx UNUSED_PARAM, + const session_t *session UNUSED_PARAM, + dtls_credentials_type_t type, + const unsigned char *id, size_t id_len, + unsigned char *result, size_t result_length) +{ + + switch (type) { + case DTLS_PSK_IDENTITY: + /* Removed due probably in the motes is useless + if (id_len) { + dtls_debug("got psk_identity_hint: '%.*s'\n", id_len, id); + } + */ + + if (result_length < psk_id_length) { + dtls_warn("cannot set psk_identity -- buffer too small\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + memcpy(result, psk_id, psk_id_length); + return psk_id_length; + case DTLS_PSK_KEY: + if (id_len != psk_id_length || memcmp(psk_id, id, id_len) != 0) { + dtls_warn("PSK for unknown id requested, exiting\n"); + return dtls_alert_fatal_create(DTLS_ALERT_ILLEGAL_PARAMETER); + } + else if (result_length < psk_key_length) { + dtls_warn("cannot set psk -- buffer too small\n"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + memcpy(result, psk_key, psk_key_length); + return psk_key_length; + default: + dtls_warn("unsupported request type: %d\n", type); + } + + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); +} +#endif /* DTLS_PSK */ + + +#ifdef DTLS_ECC +static int peer_get_ecdsa_key(struct dtls_context_t *ctx, + const session_t *session, + const dtls_ecdsa_key_t **result) +{ + (void) ctx; + (void) session; + + static const dtls_ecdsa_key_t ecdsa_key = { + .curve = DTLS_ECDH_CURVE_SECP256R1, + .priv_key = ecdsa_priv_key, + .pub_key_x = ecdsa_pub_key_x, + .pub_key_y = ecdsa_pub_key_y + }; + + *result = &ecdsa_key; + return 0; +} + +static int peer_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) key_size; + (void) other_pub_x; + (void) other_pub_y; + return 0; +} +#endif /* DTLS_ECC */ + +/** + * @brief This will try to transmit using only GNRC stack. + * This is basically the original send function from gnrc/networking + */ +static int gnrc_sending(char *addr_str, char *data, size_t data_len ) +{ + ipv6_addr_t addr; + gnrc_pktsnip_t *payload, *udp, *ip; + + /* parse destination address */ + if (ipv6_addr_from_str(&addr, addr_str) == NULL) { + puts("Error: unable to parse destination address"); + return -1; + } + + /* allocate payload */ + payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF); + + if (payload == NULL) { + puts("Error: unable to copy data to packet buffer"); + return -1; + } + + /* allocate UDP header */ + udp = gnrc_udp_hdr_build(payload, (uint16_t) CLIENT_PORT, (uint16_t) DEFAULT_PORT); + if (udp == NULL) { + puts("Error: unable to allocate UDP header"); + gnrc_pktbuf_release(payload); + return -1; + } + + /* allocate IPv6 header */ + ip = gnrc_ipv6_hdr_build(udp, NULL, &addr); + if (ip == NULL) { + puts("Error: unable to allocate IPv6 header"); + gnrc_pktbuf_release(udp); + return -1; + } + + /* + * WARNING: Too fast and the nodes dies in middle of retransmissions. + * This issue appears in the FIT-Lab (m3 motes). + * In native, is not required. + */ + xtimer_usleep(500000); + + /* send packet */ + if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) { + puts("Error: unable to locate UDP thread"); + gnrc_pktbuf_release(ip); + return -1; + } + + return 1; +} + +/** + * @brief This will print the data read from the Peer. + * THIS AT THE END of the 6 flights (DTLS App Data). + * Is here where we know that the connection has finished. + * TODO: The connected variable could de modified here. + */ +static int read_from_peer(struct dtls_context_t *ctx, + session_t *session, uint8 *data, size_t len) +{ + /* Linux and Contiki version are exactly the same. */ + (void) session; + (void) ctx; + size_t i; + printf("\n\n Echo received: "); + for (i = 0; i < len; i++) + printf("%c", data[i]); + printf(" \n\n\n"); + return 0; +} + +/** + * @brief Will try to transmit the next DTLS flight for a speicifc Peer. + * NOTE:The global buff and buflen is used for be able to transmit the + * Payload in segmented datagrams. + */ +static void try_send(struct dtls_context_t *ctx, session_t *dst) +{ + + int res; + + res = dtls_write(ctx, dst, (uint8_t *)client_payload, buflen); + + if (res >= 0) { + memmove(client_payload, client_payload + res, buflen - res); + buflen -= res; + } + else { + DEBUG("DBG-Client: dtls_write returned error!\n" ); + } +} + +/** + * @brief This SIGNAL function will prepare the next DTLS flight to send. + */ +static int send_to_peer(struct dtls_context_t *ctx, + session_t *session, uint8 *buf, size_t len) +{ + + (void) session; + /* + * For this testing with GNR we are to extract the peer's addresses and + * making the connection from zero. + */ + char *addr_str; + addr_str = (char *)dtls_get_app_data(ctx); + + + /* TODO: Confirm that indeed len size of data was sent, otherwise + * return the correct number of bytes sent + */ + gnrc_sending(addr_str, (char *)buf, len); + + return len; +} + + + +/*** + * This is a custom function for preparing the SIGNAL events and + * create a new DTLS context. + */ +static void init_dtls(session_t *dst, char *addr_str) +{ + + static dtls_handler_t cb = { + .write = send_to_peer, + .read = read_from_peer, + .event = NULL, +#ifdef DTLS_PSK + .get_psk_info = peer_get_psk_info, +#endif /* DTLS_PSK */ +#ifdef DTLS_ECC + .get_ecdsa_key = peer_get_ecdsa_key, + .verify_ecdsa_key = peer_verify_ecdsa_key +#endif /* DTLS_ECC */ + }; + +#ifdef DTLS_PSK + puts("Client support PSK"); +#endif +#ifdef DTLS_ECC + puts("Client support ECC"); +#endif + + DEBUG("DBG-Client: Debug ON"); + /* + * The objective of ctx->App is be able to retrieve + * enough information for restablishing a connection. + * This is to be used in the send_to_peer and (potentially) the + * read_from_peer, to continue the transmision with the next + * step of the DTLS flights. + * TODO: Take away the DEFAULT_PORT and CLIENT_PORT. + */ + dst->size = sizeof(ipv6_addr_t) + sizeof(unsigned short); + dst->port = (unsigned short) DEFAULT_PORT; + + if (ipv6_addr_from_str(&dst->addr, addr_str) == NULL) { + puts("ERROR: init_dtls was unable to load the IPv6 addresses!\n"); + dtls_context = NULL; + return; + } + + ipv6_addr_t addr_dbg; + ipv6_addr_from_str(&addr_dbg, addr_str); + + /*akin to syslog: EMERG, ALERT, CRITC, NOTICE, INFO, DEBUG */ + dtls_set_log_level(DTLS_LOG_NOTICE); + + dtls_context = dtls_new_context(addr_str); + if (dtls_context) { + dtls_set_handler(dtls_context, &cb); + } + + return; +} + +/** + * This is the "client" part of this program. + * Will be called each time a message is transmitted. + */ +static void client_send(char *addr_str, char *data, unsigned int delay) +{ + static int8_t iWatch; + static session_t dst; + static int connected = 0; + msg_t msg; + + gnrc_netreg_entry_t entry = GNRC_NETREG_ENTRY_INIT_PID(CLIENT_PORT, + sched_active_pid); + dtls_init(); + + if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &entry)) { + puts("Unable to register ports"); + /*FIXME: Release memory?*/ + return; + } + + if (strlen(data) > DTLS_MAX_BUF) { + puts("Data too long "); + return; + } + + init_dtls(&dst, addr_str); + if (!dtls_context) { + dtls_emerg("cannot create context\n"); + puts("Client unable to load context!"); + return; + } + + /* client_payload is global due to the SIGNAL function send_to_peer */ + client_payload = data; + buflen = strlen(client_payload); + iWatch = MAX_TIMES_TRY_TO_SEND; + + /* + * dtls_connect is the one who begin all the process. + * However, do it too fast, and the node will attend it before having a + * valid IPv6 or even a route to the destiny (This could be verified as the + * sequence number of the first DTLS Hello message will be greater than + * zero). + */ + //connected = dtls_connect(dtls_context, &dst) >= 0; + + /* + * Until all the data is not sent we remains trying to connect to the + * server. Plus a small watchdog. + */ + while ((buflen > 0) && (iWatch > 0)) { + + /* + * NOTE: I (rfuentess) personally think this should be until this point + * instead before (that is why the previous dtls_connect is by defualt + * commented.. + */ + if (!connected) { + connected = dtls_connect(dtls_context, &dst); + } + else if (connected < 0) { + puts("Client DTLS was unable to establish a channel!\n"); + /*NOTE: Not sure what to do in this scenario (if can happens)*/ + } + else { + /*TODO: must happens always or only when connected?*/ + try_send(dtls_context, &dst); + } + + /* + * WARNING: The delay is KEY HERE! Too fast, and we can kill the + * DTLS state machine. Another alternative is change to + * blocking states (making the watchdog useless) + * + * msg_receive(&msg); + */ + + xtimer_usleep(delay); + + if (msg_try_receive(&msg) == 1) { + dtls_handle_read(dtls_context, (gnrc_pktsnip_t *)(msg.content.ptr)); + } + + iWatch--; + } /*END while*/ + + dtls_free_context(dtls_context); + /* unregister our UDP listener on this thread */ + gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &entry); + connected = 0; /*Probably this should be removed or global */ + + /* Permanent or not permanent? */ + DEBUG("DTLS-Client: DTLS session finished\n"); +} + +int udp_client_cmd(int argc, char **argv) +{ + if (argc < 2) { + printf("usage: %s []\n", argv[0]); + return 1; + } + + uint32_t delay = 1000000; + if (argc < 3) { + printf("usage: %s []\n", + argv[0]); + return 1; + } + if (argc > 3) { + delay = (uint32_t)atoi(argv[3]); + } + client_send(argv[1], argv[2], delay); + + + return 0; +} diff --git a/examples/dtls-echo/dtls-server.c b/examples/dtls-echo/dtls-server.c new file mode 100644 index 000000000000..2156f7616695 --- /dev/null +++ b/examples/dtls-echo/dtls-server.c @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 The server side of TinyDTLS (Simple echo) + * + * @author Raul A. Fuentes Samaniego + * @author Olaf Bergmann + * @author Hauke Mehrtens + * @author Oliver Hahm + * + * @} + */ + +#include +#include + +#include "net/gnrc.h" +#include "net/gnrc/ipv6.h" +#include "net/gnrc/udp.h" +#include "timex.h" +#include "xtimer.h" +#include "msg.h" + + +/* TinyDTLS */ +#include "dtls.h" +#include "dtls_debug.h" +#include "tinydtls.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +//#define DEFAULT_PORT 20220 /* DTLS default port */ +#define DEFAULT_PORT 61618 /* First valid FEBx address */ + +/* TODO: MAke this local! */ +static dtls_context_t *dtls_context = NULL; + +static const unsigned char ecdsa_priv_key[] = { + 0xD9, 0xE2, 0x70, 0x7A, 0x72, 0xDA, 0x6A, 0x05, + 0x04, 0x99, 0x5C, 0x86, 0xED, 0xDB, 0xE3, 0xEF, + 0xC7, 0xF1, 0xCD, 0x74, 0x83, 0x8F, 0x75, 0x70, + 0xC8, 0x07, 0x2D, 0x0A, 0x76, 0x26, 0x1B, 0xD4 +}; + +static const unsigned char ecdsa_pub_key_x[] = { + 0xD0, 0x55, 0xEE, 0x14, 0x08, 0x4D, 0x6E, 0x06, + 0x15, 0x59, 0x9D, 0xB5, 0x83, 0x91, 0x3E, 0x4A, + 0x3E, 0x45, 0x26, 0xA2, 0x70, 0x4D, 0x61, 0xF2, + 0x7A, 0x4C, 0xCF, 0xBA, 0x97, 0x58, 0xEF, 0x9A +}; + +static const unsigned char ecdsa_pub_key_y[] = { + 0xB4, 0x18, 0xB6, 0x4A, 0xFE, 0x80, 0x30, 0xDA, + 0x1D, 0xDC, 0xF4, 0xF4, 0x2E, 0x2F, 0x26, 0x31, + 0xD0, 0x43, 0xB1, 0xFB, 0x03, 0xE2, 0x2F, 0x4D, + 0x17, 0xDE, 0x43, 0xF9, 0xF9, 0xAD, 0xEE, 0x70 +}; + + +static gnrc_netreg_entry_t server = GNRC_NETREG_ENTRY_INIT_PID( + GNRC_NETREG_DEMUX_CTX_ALL, + KERNEL_PID_UNDEF); + +#define READER_QUEUE_SIZE (8U) +char _server_stack[THREAD_STACKSIZE_MAIN + THREAD_EXTRA_STACKSIZE_PRINTF]; + +static kernel_pid_t _dtls_kernel_pid; + +/** + * @brief This care about getting messages and continue with the DTLS flights + */ +static void dtls_handle_read(dtls_context_t *ctx, gnrc_pktsnip_t *pkt) +{ + + static session_t session; + + /* + * NOTE: GNRC (Non-socket) issue: we need to modify the current + * DTLS Context for the IPv6 src (and in a future the port src). + */ + + /* Taken from the tftp server example */ + char addr_str[IPV6_ADDR_MAX_STR_LEN]; + gnrc_pktsnip_t *tmp2; + + tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6); + ipv6_hdr_t *hdr = (ipv6_hdr_t *)tmp2->data; + + ipv6_addr_to_str(addr_str, &hdr->src, sizeof(addr_str)); + /* This is unique to the server (Non-socket) */ + ctx->app = addr_str; + + /* + * TODO: More testings with TinyDTLS is neccesary, but seem this is safe. + */ + tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_UDP); + udp_hdr_t *udp = (udp_hdr_t *)tmp2->data; + + session.size = sizeof(ipv6_addr_t) + sizeof(unsigned short); + session.port = byteorder_ntohs(udp->src_port); + + ipv6_addr_from_str(&session.addr, addr_str); + + dtls_handle_message(ctx, &session, pkt->data, (unsigned int)pkt->size); + +} + + +/** + * @brief We got the TinyDTLS App Data message and answer with the same + */ +static int read_from_peer(struct dtls_context_t *ctx, + session_t *session, uint8 *data, size_t len) +{ + + +#if ENABLE_DEBUG == 1 + size_t i; + DEBUG("\nDBG-Server: Data from Client: ---"); + for (i = 0; i < len; i++) + DEBUG("%c", data[i]); + DEBUG("--- \t Sending echo..\n"); +#endif + /* echo incoming application data */ + dtls_write(ctx, session, data, len); + return 0; +} + +/** + * @brief This will try to transmit using only GNRC stack (non-socket). + */ +static int gnrc_sending(char *addr_str, char *data, size_t data_len, unsigned short rem_port ) +{ + + ipv6_addr_t addr; + gnrc_pktsnip_t *payload, *udp, *ip; + + /* parse destination address */ + if (ipv6_addr_from_str(&addr, addr_str) == NULL) { + puts("Error: unable to parse destination address"); + return -1; + } + + payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF); + + if (payload == NULL) { + puts("Error: unable to copy data to packet buffer"); + return -1; + } + + /* allocate UDP header */ + udp = gnrc_udp_hdr_build(payload, DEFAULT_PORT, rem_port); + if (udp == NULL) { + puts("Error: unable to allocate UDP header"); + gnrc_pktbuf_release(payload); + return -1; + } + + /* allocate IPv6 header */ + ip = gnrc_ipv6_hdr_build(udp, NULL, &addr); + if (ip == NULL) { + puts("Error: unable to allocate IPv6 header"); + gnrc_pktbuf_release(udp); + return -1; + } + /* send packet */ + + DEBUG("DBG-Server: Sending record to peer\n"); + + /* + * WARNING: Too fast and the nodes dies in middle of retransmissions. + * This issue appears in the FIT-Lab (m3 motes). + */ + xtimer_usleep(500000); + + /* Probably this part will be removed. **/ + if (!gnrc_netapi_dispatch_send(GNRC_NETTYPE_UDP, GNRC_NETREG_DEMUX_CTX_ALL, ip)) { + puts("Error: unable to locate UDP thread"); + gnrc_pktbuf_release(ip); + return -1; + } + + return 1; +} + +/** + * @brief We communicate with the other peer. + */ +static int send_to_peer(struct dtls_context_t *ctx, + session_t *session, uint8 *buf, size_t len) +{ + + (void) session; + + /*FIXME TODO: dtls_get_app_data(ctx) should have the remote port! */ + char *addr_str; + addr_str = (char *)dtls_get_app_data(ctx); + + gnrc_sending(addr_str, (char *)buf, len, session->port); + + return len; +} + +#ifdef DTLS_PSK +/* This function is the "key store" for tinyDTLS. It is called to + * retrieve a key for the given identity within this particular + * session. */ +static int peer_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) +{ + + (void) ctx; + (void) session; + struct keymap_t { + unsigned char *id; + size_t id_length; + unsigned char *key; + size_t key_length; + } psk[3] = { + { (unsigned char *)"Client_identity", 15, + (unsigned char *)"secretPSK", 9 }, + { (unsigned char *)"default identity", 16, + (unsigned char *)"\x11\x22\x33", 3 }, + { (unsigned char *)"\0", 2, + (unsigned char *)"", 1 } + }; + + if (type != DTLS_PSK_KEY) { + return 0; + } + + if (id) { + unsigned int i; + for (i = 0; i < sizeof(psk) / sizeof(struct keymap_t); i++) { + if (id_len == psk[i].id_length && memcmp(id, psk[i].id, id_len) == 0) { + if (result_length < psk[i].key_length) { + dtls_warn("buffer too small for PSK"); + return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR); + } + + memcpy(result, psk[i].key, psk[i].key_length); + return psk[i].key_length; + } + } + } + + return dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR); +} +#endif /* DTLS_PSK */ + +#ifdef DTLS_ECC +static int peer_get_ecdsa_key(struct dtls_context_t *ctx, + const session_t *session, + const dtls_ecdsa_key_t **result) +{ + (void) ctx; + (void) session; + static const dtls_ecdsa_key_t ecdsa_key = { + .curve = DTLS_ECDH_CURVE_SECP256R1, + .priv_key = ecdsa_priv_key, + .pub_key_x = ecdsa_pub_key_x, + .pub_key_y = ecdsa_pub_key_y + }; + + *result = &ecdsa_key; + return 0; +} + +static int peer_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_x; + (void) other_pub_y; + (void) key_size; + return 0; +} +#endif /* DTLS_ECC */ + +/** + * @brief We prepare the DTLS for this node. + */ +static void init_dtls(void) +{ + static dtls_handler_t cb = { + .write = send_to_peer, + .read = read_from_peer, + .event = NULL, +#ifdef DTLS_PSK + .get_psk_info = peer_get_psk_info, +#endif /* DTLS_PSK */ +#ifdef DTLS_ECC + .get_ecdsa_key = peer_get_ecdsa_key, + .verify_ecdsa_key = peer_verify_ecdsa_key +#endif /* DTLS_ECC */ + }; + + +#ifdef DTLS_PSK + puts("Server support PSK"); +#endif +#ifdef DTLS_ECC + puts("Server support ECC"); +#endif + + DEBUG("DBG-Server On\n"); + + /* + * The context for the server is a little different from the client. + * The simplicity of GNRC do not mix transparently with + * the DTLS Context. At this point, the server need a fresh context + * however dtls_context->app must be populated with an unknown + * IPv6 address. + * + * The non-valid Ipv6 address ( :: ) is discarded due the chaos. + * For now, the first value will be the loopback. + */ + char *addr_str = "::1"; + + /*akin to syslog: EMERG, ALERT, CRITC, NOTICE, INFO, DEBUG */ + dtls_set_log_level(DTLS_LOG_DEBUG); + + + dtls_context = dtls_new_context(addr_str); + if (dtls_context) { + dtls_set_handler(dtls_context, &cb); + } + else { + puts("Server was unable to generate DTLS Context!"); + exit(-1); + } + + + +} + +/* NOTE: wrapper or trampoline ? (Syntax question) */ + +void *dtls_server_wrapper(void *arg) +{ + (void) arg; /* TODO: Remove? We don't have args at all (NULL) */ + + msg_t _reader_queue[READER_QUEUE_SIZE]; + msg_t msg; + + /* The GNRC examples uses packet dump but we want a custom one */ + msg_init_queue(_reader_queue, READER_QUEUE_SIZE); + + init_dtls(); + + /* + * FIXME: After mutliple retransmissions, and canceled client's sessions + * the server become unable to sent NDP NA messages. Still, the TinyDTLS + * debugs seems to be fine. + */ + + while (1) { + + /* wait for a message */ + msg_receive(&msg); + + DEBUG("DBG-Server: Record Rcvd!\n"); + dtls_handle_read(dtls_context, (gnrc_pktsnip_t *)(msg.content.ptr)); + + /*TODO: What happens with other clients connecting at the same time? */ + + } /*While */ + + dtls_free_context(dtls_context); +} + +static void start_server(void) +{ + uint16_t port; + + port = (uint16_t)DEFAULT_PORT; + + (void) _dtls_kernel_pid; + + /* Only one instance of the server */ + if (server.target.pid != KERNEL_PID_UNDEF) { + printf("Error: server already running\n"); + return; + } + + /*TESTING tinydtls*/ + dtls_init(); + + /* The server is initialized */ + server.target.pid = thread_create(_server_stack, sizeof(_server_stack), + THREAD_PRIORITY_MAIN - 1, + THREAD_CREATE_STACKTEST, + dtls_server_wrapper, NULL, "DTLS Server"); + + server.demux_ctx = (uint32_t)port; + + if (gnrc_netreg_register(GNRC_NETTYPE_UDP, &server) == 0) + printf("Success: started DTLS server on port %" PRIu16 "\n", port); + else + printf("FAILURE: The UDP port is not registered!\n"); +} + +static void stop_server(void) +{ + /* check if server is running at all */ + if (server.target.pid == KERNEL_PID_UNDEF) { + printf("Error: server was not running\n"); + return; + } + + dtls_free_context(dtls_context); + + /* stop server */ + gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &server); + server.target.pid = KERNEL_PID_UNDEF; + puts("Success: stopped DTLS server"); +} + +int udp_server_cmd(int argc, char **argv) +{ + if (argc < 1) { + 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 { + puts("error: invalid command"); + } + return 0; +} diff --git a/examples/dtls-echo/main.c b/examples/dtls-echo/main.c new file mode 100644 index 000000000000..75d176b878ac --- /dev/null +++ b/examples/dtls-echo/main.c @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2015 Freie Universität Berlin + * + * 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 TinyDTLS + * + * @author Raul Fuentes <> + * + * @} + */ + + +#include + +#include "shell.h" +#include "msg.h" + + + +/*TinyDTLS WARNING check*/ +#ifdef WITH_RIOT_SOCKETS +#error TinyDTLS is configured for working with Sockets. Yet, this is non-socket +#endif + +#define MAIN_QUEUE_SIZE (8) +static msg_t _main_msg_queue[MAIN_QUEUE_SIZE]; + +/* + * Altough the server and client cna be in a simple file. + * Is more friendly to divide them + */ +extern int udp_client_cmd(int argc, char **argv); +extern int udp_server_cmd(int argc, char **argv); + +static const shell_command_t shell_commands[] = { + { "dtlsc", "Start a DTLS client", udp_client_cmd }, + { "dtlss", "Start a DTLS server (with echo)", udp_server_cmd }, + { NULL, NULL, NULL } +}; + +int main(void) +{ + /* we need a message queue for the thread running the shell in order to + * receive potentially fast incoming networking packets */ + msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE); + puts("RIOT (Tiny)DTLS testing implementation"); + + /* 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 be never reached */ + return 0; +} diff --git a/pkg/tinydtls/Makefile b/pkg/tinydtls/Makefile new file mode 100644 index 000000000000..c697c91f233c --- /dev/null +++ b/pkg/tinydtls/Makefile @@ -0,0 +1,12 @@ +PKG_NAME=tinydtls +PKG_URL=git://github.com/rfuentess/TinyDTLS.git +# PKG_VERSION=RIOT-OS +PKG_VERSION=f824b5553a865c186a9b41236be03358f0c8feaf +PKG_BUILDDIR ?= $(BINDIRBASE)/pkg/$(BOARD)/$(PKG_NAME) + +.PHONY: all + +all: git-download + $(MAKE) -C $(PKG_BUILDDIR) + +include $(RIOTBASE)/pkg/pkg.mk diff --git a/pkg/tinydtls/Makefile.include b/pkg/tinydtls/Makefile.include new file mode 100644 index 000000000000..f56e0597fa99 --- /dev/null +++ b/pkg/tinydtls/Makefile.include @@ -0,0 +1 @@ +INCLUDES += -I$(BINDIRBASE)/pkg/$(BOARD)/tinydtls