1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 06:23:53 +01:00

Merge pull request #7615 from rfuentess/dtls_upgrade

Improving TinyDTLS package and dtls-echo example
This commit is contained in:
Alexandre Abadie 2018-07-04 11:57:48 +02:00 committed by GitHub
commit a3fc323cf5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 742 additions and 740 deletions

View File

@ -9,8 +9,8 @@ RIOTBASE ?= $(CURDIR)/../..
# TinyDTLS only has support for 32-bit architectures ATM
BOARD_BLACKLIST := arduino-duemilanove arduino-mega2560 arduino-uno chronos \
msb-430 msb-430h telosb waspmote-pro wsn430-v1_3b wsn430-v1_4 \
z1 jiminy-mega256rfr2 mega-xplained
jiminy-mega256rfr2 mega-xplained msb-430 msb-430h telosb \
waspmote-pro wsn430-v1_3b wsn430-v1_4 z1
BOARD_INSUFFICIENT_MEMORY := airfy-beacon b-l072z-lrwan1 bluepill calliope-mini \
cc2650-launchpad cc2650stk hifive1 maple-mini \
@ -24,51 +24,44 @@ BOARD_INSUFFICIENT_MEMORY := airfy-beacon b-l072z-lrwan1 bluepill calliope-mini
# 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
# Specify the mandatory networking modules for IPv6 and UDP
USEMODULE += gnrc_ipv6_default
USEMODULE += gnrc_sock_udp
# 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
USEPKG += tinydtls
# UDP Port to use (20220 is default for DTLS).
DTLS_PORT ?= 20220
CFLAGS += -DDTLS_DEFAULT_PORT=$(DTLS_PORT)
# NOTE: If not 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
# Define the log entry for the tinydtls package.
# Values: 0:EMERG (Default), 1:ALERT 2:CRIT 3:WARN 4:NOTICE 5:INFO 6:DEBUG
TINYDTLS_LOG ?= 0
# Enable this line for setting tinyDTLS in debug mode.
# This overrides TINYDTLS_LOG to 6
# CFLAGS += -DTINYDTLS_DEBUG
# FIXME: This is a temporary patch
# TinyDTLS <= 0.8.6 requires around 426 bytes in RAM.
CFLAGS += -DTHREAD_STACKSIZE_MAIN=\(3*THREAD_STACKSIZE_DEFAULT\)
# TINYDTLS_EXTRA_BUFF can be used for increasing the server stack memory.
# CFLAGS += -DTINYDTLS_EXTRA_BUFF=\(512\)
# 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
# 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
CFLAGS += -DDEVELHELP
# Change this to 0 show compiler invocation lines by default:
QUIET ?= 1

View File

@ -1,16 +1,12 @@
# dtls_echo
# dtls_echo example
This example shows you how to use TinyDTLS with the non-socket approach.
This example shows how to use TinyDTLS with sock_udp.
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.
## SOCK vs. Socket
## 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.
This example is configured to use socks instead of sockets (over GNRC).
It's possible to use sockets, which give a more similar approach to the original
Linux version of TinyDTLS. However, this is not tested yet.
## Fast configuration (Between RIOT instances):
@ -29,49 +25,37 @@ Do not forget to copy the IPv6 addresses!
For the client:
PORT=tap0 make term
dtlsc <IPv6's server address> "DATA TO DATA TO DATA!"
dtlsc <IPv6's server address[%netif]> "DATA to send under encrypted channel!"
# Testings
## Boards
Those boards that do not support the `../gnrc_networking` example are included
in the `BOARD_INSUFFICIENT_MEMORY`, plus the board `cc2650stk`.
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 code has been tested in the FIT IOT-LAB tesbed with the remote
`iotlab-m3` and `iotlab-a8-m3` boards and with local `samr21-xpro` boards.
The boards that requires `periph_conf.h` are not tested.
## Handling the static memory allocation
Boards with problem type 1 (`crypto.c`):
z1
wsn430-v1_4
wsn430-v1_3b
waspmote-pro
msb-430h
msb-430
chronos
arduino-mega2560
TinyDTLS for RIOT is using the `sys/memarray` module and therefore there
are certain limits. Said resources are defined in
`tinydtls/platform-specific/riot_boards.h`, but can be overwritten at
compile time. Their default values are considered for having two DTLS
contexts (for purpose of DTLS renegotiation).
Boards with problem type 2 (`dtls_time.h`):
cc2538dk
msbiot
telosb
The resources handled by memarray are:
* `DTLS_CONTEXT_MAX` (default 2) The maximum number of DTLS context at the
same time.
* `DTLS_PEER_MAX` (default 1) The maximum number DTLS peers (i.e. sessions).
* `DTLS_HANDSHAKE_MAX` (default 1) The maximum number of concurrent DTLS handshakes.
* `DTLS_SECURITY_MAX` (the sum of the previous two) The maximum number of
concurrently used cipher keys.
* `DTLS_HASH_MAX` (Default: `3 * DTLS_PEER_MAX`) The maximum number of hash
functions that can be used in parallel.
Boards with problem type 3 (Redifinition):
saml21-xpro
samr21-xpro
arduino-uno
arduino-duemilanove
## Handling retransmissions
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.
By default, the number of transmissions of any DTLS record is settled to just
one. This can be handled by `DTLS_DEFAULT_MAX_RETRANSMIT` (defined in
`tinydtls/platform-specific/riot_boards.h`).

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* 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
@ -11,9 +12,9 @@
* @{
*
* @file
* @brief The cliend side of TinyDTLS (Simple echo)
* @brief Demonstrating the client side of TinyDTLS (Simple echo)
*
* @author Raul A. Fuentes Samaniego <ra.fuentes.sam+RIOT@gmail.com>
* @author Raul A. Fuentes Samaniego <raul.fuentes-samaniego@inria.fr>
* @author Olaf Bergmann <bergmann@tzi.org>
* @author Hauke Mehrtens <hauke@hauke-m.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
@ -23,118 +24,122 @@
#include <stdio.h>
#include <inttypes.h>
#include "net/gnrc.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h"
#include "net/gnrc/udp.h"
#include "net/gnrc/pktdump.h"
#include "timex.h"
#include "utlist.h"
#include "xtimer.h"
#include "net/sock/udp.h"
#include "tinydtls_keys.h"
/* TinyDTLS */
#include "dtls_debug.h"
#include "dtls.h"
#define ENABLE_DEBUG (0)
#include "debug.h"
/* TinyDTLS */
#include "tinydtls.h"
#include "dtls_debug.h"
#include "dtls.h"
#include "global.h"
#ifndef DTLS_DEFAULT_PORT
#define DTLS_DEFAULT_PORT 20220 /* DTLS default port */
#endif
#define CLIENT_PORT DTLS_DEFAULT_PORT + 1
#define MAX_TIMES_TRY_TO_SEND 10 /* Expected to be 1 - 255 */
/* TODO: Remove the UNUSED_PARAM from TinyDTLS' stack? */
#ifdef __GNUC__
#define UNUSED_PARAM __attribute__((unused))
/* Delay to give time to the remote peer to do the compute (client only). */
#ifdef DTLS_ECC
#define DEFAULT_US_DELAY 10000000
#else
#define UNUSED_PARAM
#endif /* __GNUC__ */
#define DEFAULT_US_DELAY 100
#endif
#ifdef DTLS_PSK
static int dtls_connected = 0; /* This is handled by Tinydtls callbacks */
#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)
/* TinyDTLS callback for detecting the state of the DTLS channel. */
static int _events_handler(struct dtls_context_t *ctx,
session_t *session,
dtls_alert_level_t level,
unsigned short code)
{
(void) ctx;
(void) session;
(void) level;
if (code == DTLS_EVENT_CONNECTED) {
dtls_connected = 1;
DEBUG("CLIENT: DTLS Channel established!\n");
}
/* At least a DTLS Client Hello was prepared? */
else if ((ENABLE_DEBUG) && (code == DTLS_EVENT_CONNECT)) {
DEBUG("CLIENT: DTLS Channel started\n");
}
/* NOTE: DTLS_EVENT_RENEGOTIATE can be handled here */
return 0;
}
/*
* Handles all the packets arriving at the node and identifies those that are
* DTLS records. Also, it determines if said DTLS record is coming from a new
* peer or a currently established peer.
*
*/
static void dtls_handle_read(dtls_context_t *ctx)
{
static session_t session;
static sock_udp_ep_t remote = SOCK_IPV6_EP_ANY;
uint8_t packet_rcvd[DTLS_MAX_BUF];
/*
* NOTE: GNRC (Non-socket) issue: we need to modify the current
* DTLS Context for the IPv6 src (and in a future the port src).
*/
if (!ctx) {
DEBUG("%s: No DTLS context\n", __func__);
return;
}
/* Taken from the tftp server example */
char addr_str[IPV6_ADDR_MAX_STR_LEN];
gnrc_pktsnip_t *tmp2;
if (!dtls_get_app_data(ctx)) {
DEBUG("%s: No app_data stored!\n", __func__);
return;
}
tmp2 = gnrc_pktsnip_search_type(pkt, GNRC_NETTYPE_IPV6);
ipv6_hdr_t *hdr = (ipv6_hdr_t *)tmp2->data;
sock_udp_t *sock;
sock = (sock_udp_t *)dtls_get_app_data(ctx);
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;
if (sock_udp_get_remote(sock, &remote) == -ENOTCONN) {
DEBUG("%s: Unable to retrieve remote!\n", __func__);
return;
}
session.size = sizeof(ipv6_addr_t) + sizeof(unsigned short);
session.port = byteorder_ntohs(udp->src_port);
ssize_t res = sock_udp_recv(sock, packet_rcvd, DTLS_MAX_BUF,
1 * US_PER_SEC + DEFAULT_US_DELAY,
&remote);
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));
if (res <= 0) {
if ((ENABLE_DEBUG) && (res != -EAGAIN) && (res != -ETIMEDOUT)) {
DEBUG("sock_udp_recv unexepcted code error: %i\n", (int)res);
}
return;
}
ipv6_addr_from_str(&session.addr, addr_str);
/* session requires the remote socket (IPv6:UDP) address and netif */
session.size = sizeof(uint8_t) * 16 + sizeof(unsigned short);
session.port = remote.port;
if (&remote.netif == SOCK_ADDR_ANY_NETIF) {
session.ifindex = SOCK_ADDR_ANY_NETIF;
}
else {
session.ifindex = remote.netif;
}
dtls_handle_message(ctx, &session, pkt->data, (unsigned int)pkt->size);
if (memcpy(&session.addr, &remote.addr.ipv6, 16) == NULL) {
puts("ERROR: memcpy failed!");
return;
}
if (ENABLE_DEBUG) {
DEBUG("DBG-Client: Msg received from \n\t Addr Src: [");
ipv6_addr_print(&session.addr);
DEBUG("]:%u\n", remote.port);
}
dtls_handle_message(ctx, &session, packet_rcvd, (int)DTLS_MAX_BUF);
return;
}
#ifdef DTLS_PSK
@ -143,25 +148,25 @@ 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)
static int _peer_get_psk_info_handler(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;
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 (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");
@ -190,15 +195,16 @@ static int peer_get_psk_info(struct dtls_context_t *ctx UNUSED_PARAM,
}
#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)
static int _peer_get_ecdsa_key_handler(struct dtls_context_t *ctx,
const session_t *session,
const dtls_ecdsa_key_t **result)
{
(void) ctx;
(void) session;
/* TODO: Load the key from external source */
static const dtls_ecdsa_key_t ecdsa_key = {
.curve = DTLS_ECDH_CURVE_SECP256R1,
.priv_key = ecdsa_priv_key,
@ -210,325 +216,278 @@ static int peer_get_ecdsa_key(struct dtls_context_t *ctx,
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)
static int _peer_verify_ecdsa_key_handler(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;
(void) other_pub_x;
(void) key_size;
/* TODO: As far for tinyDTLS 0.8.2 this is not used */
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 )
/* Reception of a DTLS Application data record. */
static int _read_from_peer_handler(struct dtls_context_t *ctx,
session_t *session,
uint8 *data, size_t len)
{
int iface;
ipv6_addr_t addr;
gnrc_pktsnip_t *payload, *udp, *ip;
(void) ctx;
(void) session;
/* get interface, if available */
iface = ipv6_addr_split_iface(addr_str);
if ((iface < 0) && (gnrc_netif_numof() == 1)) {
iface = gnrc_netif_iter(NULL)->pid;
}
/* 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;
}
/* add netif header, if interface was given */
if (iface > 0) {
gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
((gnrc_netif_hdr_t *)netif->data)->if_pid = (kernel_pid_t)iface;
LL_PREPEND(ip, netif);
}
printf("Client: got DTLS Data App -- ");
for (size_t i = 0; i < len; i++)
printf("%c", data[i]);
puts(" --");
/*
* 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.
* NOTE: To answer the other peer uses dtls_write(). E.g.
* return dtls_write(ctx, session, data, len);
*/
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)
/* Transmits the upper layer data data in one or more DTLS Data App records . */
ssize_t try_send(struct dtls_context_t *ctx, session_t *dst, uint8 *buf, size_t len)
{
int res = 0;
int res;
res = dtls_write(ctx, dst, (uint8_t *)client_payload, buflen);
res = dtls_write(ctx, dst, buf, len);
if (res >= 0) {
memmove(client_payload, client_payload + res, buflen - res);
buflen -= res;
memmove(buf, buf + res, len - res);
len -= res;
return len;
}
else {
DEBUG("DBG-Client: dtls_write returned error!\n" );
else if (res < 0) {
dtls_crit("Client: dtls_write returned error!\n");
return -1;
}
return 0;
}
/**
* @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)
/* Handles the DTLS communication with the other peer. */
static int _send_to_peer_handler(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);
assert(ctx);
assert(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);
if (!dtls_get_app_data(ctx)) {
return -1; /* At this point this should not happen anymore. */
}
return len;
sock_udp_t *sock;
sock = (sock_udp_t *)dtls_get_app_data(ctx);
ssize_t res = sock_udp_send(sock, buf, len, NULL);
if (res <= 0) {
puts("ERROR: Unable to send DTLS record");
}
return res;
}
/***
* 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)
/* DTLS variables are initialized. */
dtls_context_t *_init_dtls(sock_udp_t *sock, sock_udp_ep_t *local,
sock_udp_ep_t *remote, session_t *dst,
char *addr_str)
{
dtls_context_t *new_context = NULL;
static dtls_handler_t cb = {
.write = send_to_peer,
.read = read_from_peer,
.event = NULL,
.write = _send_to_peer_handler,
.read = _read_from_peer_handler,
.event = _events_handler,
#ifdef DTLS_PSK
.get_psk_info = peer_get_psk_info,
.get_psk_info = _peer_get_psk_info_handler,
#endif /* DTLS_PSK */
#ifdef DTLS_ECC
.get_ecdsa_key = peer_get_ecdsa_key,
.verify_ecdsa_key = peer_verify_ecdsa_key
.get_ecdsa_key = _peer_get_ecdsa_key_handler,
.verify_ecdsa_key = _peer_verify_ecdsa_key_handler
#endif /* DTLS_ECC */
};
#ifdef DTLS_PSK
puts("Client support PSK");
DEBUG("Client support PSK\n");
#endif
#ifdef DTLS_ECC
puts("Client support ECC");
DEBUG("Client support ECC\n");
#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;
dtls_connected = 0;
#ifdef TINYDTLS_LOG_LVL
dtls_set_log_level(TINYDTLS_LOG_LVL);
#endif
/* First, we prepare the UDP Sock */
local->port = (unsigned short) CLIENT_PORT;
remote->port = (unsigned short) DTLS_DEFAULT_PORT;
/* Parsing <address>[:<iface>]:Port */
int iface = ipv6_addr_split_iface(addr_str);
if (iface == -1) {
if (gnrc_netif_numof() == 1) {
/* assign the single interface found in gnrc_netif_numof() */
dst->ifindex = (uint16_t)gnrc_netif_iter(NULL)->pid;
remote->netif = (uint16_t)gnrc_netif_iter(NULL)->pid;
}
else {
/* FIXME This probably is not valid with multiple interfaces */
dst->ifindex = remote->netif;
}
}
else {
if (gnrc_netif_get_by_pid(iface) == NULL) {
puts("ERROR: interface not valid");
return new_context;
}
dst->ifindex = (uint16_t)gnrc_netif_iter(NULL)->pid;
remote->netif = (uint16_t)gnrc_netif_iter(NULL)->pid;
}
if (ipv6_addr_from_str((ipv6_addr_t *)remote->addr.ipv6, addr_str) == NULL) {
puts("ERROR: unable to parse destination address");
return new_context;
}
/* Second: We prepare the DTLS Session by means of ctx->app */
dst->size = sizeof(uint8_t) * 16 + sizeof(unsigned short);
dst->port = remote->port;
/* NOTE: remote.addr.ipv6 and dst->addr are different structures. */
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;
puts("ERROR: init_dtls was unable to load the IPv6 addresses!");
return new_context;
}
new_context = dtls_new_context(sock);
if (new_context) {
dtls_set_handler(new_context, &cb);
}
return new_context;
}
static void client_send(char *addr_str, char *data)
{
static session_t dst;
dtls_context_t *dtls_context = NULL;
sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
sock_udp_ep_t remote = SOCK_IPV6_EP_ANY;
sock_udp_t sock;
uint8_t watch = MAX_TIMES_TRY_TO_SEND;
ssize_t app_data_buf = 0; /* Upper layer packet to send */
/* NOTE: dtls_init() must be called previous to this (see main.c) */
dtls_context = _init_dtls(&sock, &local, &remote, &dst, addr_str);
if (!dtls_context) {
puts("ERROR: Client unable to load context!");
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);
char *client_payload;
if (strlen(data) > DTLS_MAX_BUF) {
puts("ERROR: Exceeded max size of DTLS buffer.");
return;
}
client_payload = data;
app_data_buf = strlen(client_payload);
/* The sock must be opened with the remote already linked to it */
if (sock_udp_create(&sock, &local, &remote, 0) != 0) {
puts("ERROR: Unable to create UDP sock");
return;
}
/*
* Starts the DTLS handshake process by sending the first DTLS Hello Client
* record.
*
* NOTE: If dtls_connect() returns zero, then the DTLS channel for the
* dtls_context is already created (never the case for this example)
*/
if (dtls_connect(dtls_context, &dst) < 0) {
puts("ERROR: Client unable to start a DTLS channel!\n");
return;
}
/*
* This loop transmits all the DTLS records involved in the DTLS session.
* Including the real (upper) data to send and to receive. There is a
* watchdog if the remote peer stop answering.
*
* Max lifetime expected for a DTLS handshake is 10 sec. This is reflected
* with the variable watch and the timeout for sock_udp_recv().
*
* NOTE: DTLS Sessions can handles more than one single node but by
* default is limited to a single peer with a single context and
* a single concurrent handshake.
* See tinydtls/platform-specific/riot_boards.h for more info.
* NOTE: DTLS_DEFAULT_MAX_RETRANSMIT has an impact here.
*/
while ((app_data_buf > 0) && (watch > 0)) {
/* DTLS Session must be established before sending our data */
if (dtls_connected) {
DEBUG("Sending (upper layer) data\n");
app_data_buf = try_send(dtls_context, &dst,
(uint8 *)client_payload, app_data_buf);
if (app_data_buf == 0) { /* Client only transmit data one time. */
watch = 0;
}
}
/* Check if a DTLS record was received */
/* NOTE: We expect an answer after try_send() */
dtls_handle_read(dtls_context);
watch--;
} /* END while */
/*
* BUG: tinyDTLS (<= 0.8.6)
* If dtls_connect() is called but the handshake does not complete (e.g.
* peer is offline) then a netq_t object is allocated and never freed
* leaving a memory leak of 124 bytes.
* This can lead to "retransmit buffer full" error.
*
* A temporary solution is to make the dtls_context_t global and be sure
* to never release it. Alternatively, never let this part of the code
* ends, in a similar approach to the server side.
*/
/* Release resources (strict order!) */
dtls_free_context(dtls_context); /* This also sends a DTLS Alert record */
sock_udp_close(&sock);
dtls_connected = 0;
DEBUG("Client DTLS session finished\n");
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)
{
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)
{
uint32_t delay = 1000000;
if (argc < 3) {
printf("usage: %s <addr> <data> [<delay in us>]\n", argv[0]);
if (argc != 3) {
printf("usage: %s <addr> <data> \n", argv[0]);
return 1;
}
else if (argc > 3) {
delay = atoi(argv[3]);
}
client_send(argv[1], argv[2], delay);
client_send(argv[1], argv[2]);
return 0;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* 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
@ -11,9 +12,9 @@
* @{
*
* @file
* @brief The server side of TinyDTLS (Simple echo)
* @brief Demonstrating the server side of TinyDTLS (Simple echo)
*
* @author Raul A. Fuentes Samaniego <ra.fuentes.sam+RIOT@gmail.com>
* @author Raul A. Fuentes Samaniego <ra.fuentes.sam+RIOT@gmail.com>
* @author Olaf Bergmann <bergmann@tzi.org>
* @author Hauke Mehrtens <hauke@hauke-m.de>
* @author Oliver Hahm <oliver.hahm@inria.fr>
@ -24,16 +25,9 @@
#include <stdio.h>
#include <inttypes.h>
#include "net/gnrc.h"
#include "net/gnrc/ipv6.h"
#include "net/gnrc/netif.h"
#include "net/gnrc/netif/hdr.h"
#include "net/gnrc/udp.h"
#include "timex.h"
#include "utlist.h"
#include "xtimer.h"
#include "net/sock/udp.h"
#include "msg.h"
#include "tinydtls_keys.h"
/* TinyDTLS */
#include "dtls.h"
@ -43,210 +37,167 @@
#define ENABLE_DEBUG (0)
#include "debug.h"
//#define DEFAULT_PORT 20220 /* DTLS default port */
#define DEFAULT_PORT 61618 /* First valid FEBx address */
#ifndef DTLS_DEFAULT_PORT
#define DTLS_DEFAULT_PORT 20220 /* DTLS default port */
#endif
/* TODO: MAke this local! */
static dtls_context_t *dtls_context = NULL;
#define DTLS_STOP_SERVER_MSG 0x4001 /* Custom IPC type msg. */
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
};
/*
* This structure will be used for storing the sock and the remote into the
* dtls_context_t variable.
*
* This is because remote must not have port set to zero on sock_udp_create()
* making impossible to recover the remote with sock_udp_get_remote()
*
* An alternative is to modify dtls_handle_message () to receive the remote
* from sock_udp_recv(). Also, it's required to modify _send_to_peer_handler() for
* parsing an auxiliary sock_udp_ep_t variable from the dls session.
*/
typedef struct {
sock_udp_t *sock;
sock_udp_ep_t *remote;
} dtls_remote_peer_t;
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);
static kernel_pid_t _dtls_server_pid = 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");
/* NOTE: Temporary patch for tinyDTLS 0.8.6 */
#ifndef TINYDTLS_EXTRA_BUFF
#define TINYDTLS_EXTRA_BUFF (0U)
#endif
/* echo incoming application data */
dtls_write(ctx, session, data, len);
return 0;
char _dtls_server_stack[THREAD_STACKSIZE_MAIN +
THREAD_EXTRA_STACKSIZE_PRINTF +
TINYDTLS_EXTRA_BUFF];
/*
* Handles all the packets arriving at the node and identifies those that are
* DTLS records. Also, it determines if said DTLS record is coming from a new
* peer or a currently established peer.
*/
static void dtls_handle_read(dtls_context_t *ctx)
{
static session_t session;
static uint8_t packet_rcvd[DTLS_MAX_BUF];
assert(ctx);
assert(dtls_get_app_data(ctx));
if (!ctx) {
DEBUG("No DTLS context!\n");
return;
}
if (!dtls_get_app_data(ctx)) {
DEBUG("No app_data stored!\n");
return;
}
dtls_remote_peer_t *remote_peer;
remote_peer = (dtls_remote_peer_t *)dtls_get_app_data(ctx);
ssize_t res = sock_udp_recv(remote_peer->sock, packet_rcvd, DTLS_MAX_BUF,
1 * US_PER_SEC, remote_peer->remote);
if (res <= 0) {
if ((ENABLE_DEBUG) && (res != -EAGAIN) && (res != -ETIMEDOUT)) {
DEBUG("sock_udp_recv unexepcted code error: %i\n", (int)res);
}
return;
}
DEBUG("DBG-Server: Record Rcvd\n");
/* (DTLS) session requires the remote peer address (IPv6:Port) and netif */
session.size = sizeof(uint8_t) * 16 + sizeof(unsigned short);
session.port = remote_peer->remote->port;
if (remote_peer->remote->netif == SOCK_ADDR_ANY_NETIF) {
session.ifindex = SOCK_ADDR_ANY_NETIF;
}
else {
session.ifindex = remote_peer->remote->netif;
}
if (memcpy(&session.addr, &remote_peer->remote->addr.ipv6, 16) == NULL) {
puts("ERROR: memcpy failed!");
return;
}
dtls_handle_message(ctx, &session, packet_rcvd, (int)DTLS_MAX_BUF);
return;
}
/**
* @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 )
/* Reception of a DTLS Application data record. */
static int _read_from_peer_handler(struct dtls_context_t *ctx,
session_t *session, uint8 *data, size_t len)
{
int iface;
ipv6_addr_t addr;
gnrc_pktsnip_t *payload, *udp, *ip;
size_t i;
/* get interface, if available */
iface = ipv6_addr_split_iface(addr_str);
if ((iface < 0) && (gnrc_netif_numof() == 1)) {
iface = gnrc_netif_iter(NULL)->pid;
}
/* parse destination address */
if (ipv6_addr_from_str(&addr, addr_str) == NULL) {
puts("Error: unable to parse destination address");
return -1;
printf("\nServer: got DTLS Data App: --- ");
for (i = 0; i < len; i++) {
printf("%c", data[i]);
}
puts(" ---\t(echo!)");
payload = gnrc_pktbuf_add(NULL, data, data_len, GNRC_NETTYPE_UNDEF);
/* echo back the application data rcvd. */
return dtls_write(ctx, session, data, len);
}
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;
}
/* add netif header, if interface was given */
if (iface > 0) {
gnrc_pktsnip_t *netif = gnrc_netif_hdr_build(NULL, 0, NULL, 0);
((gnrc_netif_hdr_t *)netif->data)->if_pid = (kernel_pid_t)iface;
LL_PREPEND(ip, netif);
}
/* send packet */
DEBUG("DBG-Server: Sending record to peer\n");
/* Handles the DTLS communication with the other peer. */
static int _send_to_peer_handler(struct dtls_context_t *ctx,
session_t *session, uint8 *buf, size_t len)
{
/*
* WARNING: Too fast and the nodes dies in middle of retransmissions.
* This issue appears in the FIT-Lab (m3 motes).
* It's possible to create a sock_udp_ep_t variable. But, it's required
* to copy memory from the session variable to it.
*/
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);
assert(ctx);
assert(dtls_get_app_data(ctx));
gnrc_sending(addr_str, (char *)buf, len, session->port);
if (!dtls_get_app_data(ctx)) {
return -1;
}
return len;
dtls_remote_peer_t *remote_peer;
remote_peer = (dtls_remote_peer_t *)dtls_get_app_data(ctx);
DEBUG("DBG-Server: Sending record\n");
return sock_udp_send(remote_peer->sock, buf, len, remote_peer->remote);
}
#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)
{
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_handler(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 *)psk_id, psk_id_length,
(unsigned char *)psk_key, psk_key_length },
{ (unsigned char *)"default identity", 16,
(unsigned char *)"\x11\x22\x33", 3 },
{ (unsigned char *)"\0", 2,
@ -258,7 +209,7 @@ static int peer_get_psk_info(struct dtls_context_t *ctx, const session_t *sessio
}
if (id) {
unsigned int i;
uint8_t 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) {
@ -277,9 +228,9 @@ static int peer_get_psk_info(struct dtls_context_t *ctx, const session_t *sessio
#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)
static int _peer_get_ecdsa_key_handler(struct dtls_context_t *ctx,
const session_t *session,
const dtls_ecdsa_key_t **result)
{
(void) ctx;
(void) session;
@ -290,162 +241,184 @@ static int peer_get_ecdsa_key(struct dtls_context_t *ctx,
.pub_key_y = ecdsa_pub_key_y
};
/* TODO: Load the key from external source */
*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)
static int _peer_verify_ecdsa_key_handler(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;
/* TODO: As far for tinyDTLS 0.8.2 this is not used */
return 0;
}
#endif /* DTLS_ECC */
/**
* @brief We prepare the DTLS for this node.
*/
static void init_dtls(void)
/* DTLS variables and register are initialized. */
dtls_context_t *_server_init_dtls(dtls_remote_peer_t *remote_peer)
{
dtls_context_t *new_context = NULL;
static dtls_handler_t cb = {
.write = send_to_peer,
.read = read_from_peer,
.write = _send_to_peer_handler,
.read = _read_from_peer_handler,
.event = NULL,
#ifdef DTLS_PSK
.get_psk_info = peer_get_psk_info,
.get_psk_info = _peer_get_psk_info_handler,
#endif /* DTLS_PSK */
#ifdef DTLS_ECC
.get_ecdsa_key = peer_get_ecdsa_key,
.verify_ecdsa_key = peer_verify_ecdsa_key
.get_ecdsa_key = _peer_get_ecdsa_key_handler,
.verify_ecdsa_key = _peer_verify_ecdsa_key_handler
#endif /* DTLS_ECC */
};
#ifdef DTLS_PSK
puts("Server support PSK");
DEBUG("Server support PSK\n");
#endif
#ifdef DTLS_ECC
puts("Server support ECC");
DEBUG("Server support ECC\n");
#endif
DEBUG("DBG-Server On\n");
#ifdef TINYDTLS_LOG_LVL
dtls_set_log_level(TINYDTLS_LOG_LVL);
#endif
/*
* 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.
* The context for the server is different from the client.
* This is because sock_udp_create() cannot work with a remote endpoint
* with port set to 0. And even after sock_udp_recv(), sock_udp_get_remote()
* cannot retrieve the remote.
*/
char *addr_str = "::1";
new_context = dtls_new_context(remote_peer);
/*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);
if (new_context) {
dtls_set_handler(new_context, &cb);
}
else {
puts("Server was unable to generate DTLS Context!");
exit(-1);
return NULL;
}
return new_context;
}
/* NOTE: wrapper or trampoline ? (Syntax question) */
void *dtls_server_wrapper(void *arg)
void *_dtls_server_wrapper(void *arg)
{
(void) arg; /* TODO: Remove? We don't have args at all (NULL) */
(void) arg;
bool active = true;
msg_t _reader_queue[READER_QUEUE_SIZE];
msg_t msg;
/* The GNRC examples uses packet dump but we want a custom one */
sock_udp_t udp_socket;
sock_udp_ep_t local = SOCK_IPV6_EP_ANY;
sock_udp_ep_t remote = SOCK_IPV6_EP_ANY;
dtls_context_t *dtls_context = NULL;
dtls_remote_peer_t remote_peer;
remote_peer.sock = &udp_socket;
remote_peer.remote = &remote;
/* Prepare (thread) messages reception */
msg_init_queue(_reader_queue, READER_QUEUE_SIZE);
init_dtls();
/* NOTE: dtls_init() must be called previous to this (see main.c) */
/*
* 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.
*/
local.port = DTLS_DEFAULT_PORT;
ssize_t res = sock_udp_create(&udp_socket, &local, NULL, 0);
if (res == -1) {
puts("ERROR: Unable create sock.");
return (void *) NULL;
}
while (1) {
dtls_context = _server_init_dtls(&remote_peer);
/* wait for a message */
msg_receive(&msg);
if (!dtls_context) {
puts("ERROR: Server unable to load context!");
return (void *) NULL;
}
DEBUG("DBG-Server: Record Rcvd!\n");
dtls_handle_read(dtls_context, (gnrc_pktsnip_t *)(msg.content.ptr));
while (active) {
/*TODO: What happens with other clients connecting at the same time? */
msg_try_receive(&msg); /* Check if we got an (thread) message */
if (msg.type == DTLS_STOP_SERVER_MSG) {
active = false;
}
else {
/* Listening for any DTLS recodrd */
dtls_handle_read(dtls_context);
}
}
} /*While */
/* Release resources (strict order) */
dtls_free_context(dtls_context); /* This also sends a DTLS Alert record */
sock_udp_close(&udp_socket);
msg_reply(&msg, &msg); /* Basic answer to the main thread */
dtls_free_context(dtls_context);
return (void *) NULL;
}
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");
if (_dtls_server_pid != KERNEL_PID_UNDEF) {
puts("Error: server already running");
return;
}
/*TESTING tinydtls*/
dtls_init();
/* The server is initialized */
_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");
/* 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");
/* Uncommon but better be sure */
if (_dtls_server_pid == EINVAL) {
puts("ERROR: Thread invalid");
_dtls_server_pid = KERNEL_PID_UNDEF;
return;
}
server.demux_ctx = (uint32_t)port;
if (_dtls_server_pid == EOVERFLOW) {
puts("ERROR: Thread overflow!");
_dtls_server_pid = KERNEL_PID_UNDEF;
return;
}
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");
return;
}
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");
if (_dtls_server_pid == KERNEL_PID_UNDEF) {
puts("Error: DTLS server is not running");
return;
}
dtls_free_context(dtls_context);
/* prepare the stop message */
msg_t m;
m.type = DTLS_STOP_SERVER_MSG;
/* stop server */
gnrc_netreg_unregister(GNRC_NETTYPE_UDP, &server);
server.target.pid = KERNEL_PID_UNDEF;
puts("Success: stopped DTLS server");
DEBUG("Stopping server...\n");
/* 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 udp_server_cmd(int argc, char **argv)
@ -461,7 +434,7 @@ int udp_server_cmd(int argc, char **argv)
stop_server();
}
else {
puts("error: invalid command");
printf("Error: invalid command. Usage: %s start|stop\n", argv[0]);
}
return 0;
}

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2015 Freie Universität Berlin
* 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
@ -19,32 +20,27 @@
* @}
*/
#include <stdio.h>
#include "shell.h"
#include "msg.h"
#include "dtls.h"
/*TinyDTLS WARNING check*/
/* TinyDTLS WARNING check */
#ifdef WITH_RIOT_SOCKETS
#error TinyDTLS is configured for working with Sockets. Yet, this is non-socket
#error TinyDTLS is set to use sockets but the app is configured for socks.
#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 },
{ "dtlss", "Start and stop a DTLS server", udp_server_cmd },
{ NULL, NULL, NULL }
};
@ -55,6 +51,9 @@ int main(void)
msg_init_queue(_main_msg_queue, MAIN_QUEUE_SIZE);
puts("RIOT (Tiny)DTLS testing implementation");
/* TinyDTLS settings (Universal and called only one time by reboot) */
dtls_init();
/* start shell */
puts("All up, running the shell now");
char line_buf[SHELL_DEFAULT_BUFSIZE];

View File

@ -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-echo example.
*
* @author Raul Fuentes <raul.fuentes-samaniego@inria.fr>
*
* @}
*/
#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 */

View File

@ -1,7 +1,6 @@
PKG_NAME=tinydtls
PKG_URL=https://github.com/rfuentess/TinyDTLS.git
# PKG_VERSION=RIOT-OS
PKG_VERSION=eb6f017ab451bb6cc4428b3e449955a76aeeba19
PKG_URL=https://git.eclipse.org/r/tinydtls/org.eclipse.tinydtls
PKG_VERSION=84f1f4e3ca13101a5a9aedeaf08039636c4f34dd
PKG_LICENSE=EPL-1.0,EDL-1.0
CFLAGS += -Wno-implicit-fallthrough
@ -15,7 +14,6 @@ all: git-download
@cp $(PKG_BUILDDIR)/Makefile.riot $(PKG_BUILDDIR)/Makefile
@cp $(PKG_BUILDDIR)/aes/Makefile.riot $(PKG_BUILDDIR)/aes/Makefile
@cp $(PKG_BUILDDIR)/ecc/Makefile.riot $(PKG_BUILDDIR)/ecc/Makefile
@cp $(PKG_BUILDDIR)/sha2/Makefile.riot $(PKG_BUILDDIR)/sha2/Makefile
"$(MAKE)" -C $(PKG_BUILDDIR)
include $(RIOTBASE)/pkg/pkg.mk

View File

@ -3,7 +3,8 @@ ifneq (,$(filter tinydtls,$(USEPKG)))
endif
ifneq (,$(filter tinydtls,$(USEMODULE)))
USEMODULE += memarray
USEMODULE += hashes
USEMODULE += tinydtls_aes
USEMODULE += tinydtls_ecc
USEMODULE += tinydtls_sha2
endif

View File

@ -6,6 +6,39 @@ ifeq ($(TOOLCHAIN), llvm)
CFLAGS += -Wno-gnu-zero-variadic-macro-arguments -Wno-unused-function
endif
ifneq (,$(filter tinydtls,$(USEMODULE)))
INCLUDES += -I$(PKG_BUILDDIR)
# Mandatory for tinyDTLS
CFLAGS += -DDTLSv12 -DWITH_SHA256
# Dependencies partially under control of the App's requirements
# The configuration for socket overrides Sock
ifeq (,$(filter WITH_RIOT_SOCKETS,$(CFLAGS)))
CFLAGS += -DWITH_RIOT_GNRC
endif
# NOTE: PSK should be enabled by default BUT if the user define any other cipher
# suite(s) it should not be enabled.
# TODO: Create the flag DTLS_CIPHERS with keywords PSK, ECC (and future)
ifeq (,$(filter -DDTLS_PSK,$(CFLAGS)))
ifeq (,$(filter -DDTLS_ECC,$(CFLAGS)))
CFLAGS += -DDTLS_PSK
endif
endif
# Handles the verbosity of tinyDTLS. Default: Minimum or just error messages.
ifeq (,$(filter -DTINYDTLS_DEBUG,$(CFLAGS)))
ifeq ( , $(TINYDTLS_LOG))
CFLAGS += -DTINYDTLS_LOG_LVL=0
else
CFLAGS += -DTINYDTLS_LOG_LVL=$(TINYDTLS_LOG)
endif
else
CFLAGS += -DTINYDTLS_LOG_LVL=6
endif
endif
ifneq (,$(filter tinydtls_aes,$(USEMODULE)))
DIRS += $(PKG_BUILDDIR)/aes
endif
@ -13,7 +46,3 @@ endif
ifneq (,$(filter tinydtls_ecc,$(USEMODULE)))
DIRS += $(PKG_BUILDDIR)/ecc
endif
ifneq (,$(filter tinydtls_sha2,$(USEMODULE)))
DIRS += $(PKG_BUILDDIR)/sha2
endif