1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/examples/dtls-echo/dtls-server.c

439 lines
12 KiB
C

/*
* 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
* directory for more details.
*/
/**
* @ingroup examples
* @{
*
* @file
* @brief Demonstrating the server side of TinyDTLS (Simple echo)
*
* @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>
*
* @}
*/
#include <assert.h>
#include <stdio.h>
#include <inttypes.h>
#include "net/sock/udp.h"
#include "msg.h"
#include "timex.h"
#include "tinydtls_keys.h"
/* TinyDTLS */
#include "dtls.h"
#include "dtls_debug.h"
#include "tinydtls.h"
#define ENABLE_DEBUG 0
#include "debug.h"
#ifndef DTLS_DEFAULT_PORT
#define DTLS_DEFAULT_PORT 20220 /* DTLS default port */
#endif
#define DTLS_STOP_SERVER_MSG 0x4001 /* Custom IPC type msg. */
/*
* 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 kernel_pid_t _dtls_server_pid = KERNEL_PID_UNDEF;
#define READER_QUEUE_SIZE (8U)
/* NOTE: Temporary patch for tinyDTLS 0.8.6 */
#ifndef TINYDTLS_EXTRA_BUFF
#define TINYDTLS_EXTRA_BUFF (0U)
#endif
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 int 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 0;
}
if (!dtls_get_app_data(ctx)) {
DEBUG("No app_data stored!\n");
return 0;
}
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,
sizeof(packet_rcvd), 1 * US_PER_SEC,
remote_peer->remote);
if (res <= 0) {
if ((ENABLE_DEBUG) && (res != -EAGAIN) && (res != -ETIMEDOUT)) {
DEBUG("sock_udp_recv unexpected code error: %" PRIiSIZE "\n", res);
}
return 0;
}
DEBUG("DBG-Server: Record Rcvd\n");
/* (DTLS) session requires the remote peer address (IPv6:Port) and netif */
dtls_session_init(&session);
session.addr.port = remote_peer->remote->port;
session.addr.family = AF_INET6;
if (remote_peer->remote->netif == SOCK_ADDR_ANY_NETIF) {
session.ifindex = SOCK_ADDR_ANY_NETIF;
}
else {
session.ifindex = remote_peer->remote->netif;
}
memcpy(&session.addr.ipv6, &remote_peer->remote->addr.ipv6, sizeof(session.addr.ipv6));
return dtls_handle_message(ctx, &session, packet_rcvd, res);
}
/* 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)
{
size_t i;
printf("\nServer: got DTLS Data App: --- ");
for (i = 0; i < len; i++) {
printf("%c", data[i]);
}
puts(" ---\t(echo!)");
/* echo back the application data rcvd. */
return dtls_write(ctx, session, data, 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)
{
/*
* It's possible to create a sock_udp_ep_t variable. But, it's required
* to copy memory from the session variable to it.
*/
(void) session;
assert(ctx);
assert(dtls_get_app_data(ctx));
if (!dtls_get_app_data(ctx)) {
return -1;
}
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 CONFIG_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_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 *)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,
(unsigned char *)"", 1 }
};
if (type != DTLS_PSK_KEY) {
return 0;
}
if (id) {
uint8_t i;
for (i = 0; i < ARRAY_SIZE(psk); 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 /* CONFIG_DTLS_PSK */
#ifdef CONFIG_DTLS_ECC
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;
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
};
/* TODO: Load the key from external source */
*result = &ecdsa_key;
return 0;
}
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 /* CONFIG_DTLS_ECC */
/* DTLS variables and register are initialized. */
dtls_context_t *_server_init_dtls(dtls_remote_peer_t *remote_peer)
{
dtls_context_t *new_context;
static dtls_handler_t cb = {
.write = _send_to_peer_handler,
.read = _read_from_peer_handler,
.event = NULL,
#ifdef CONFIG_DTLS_PSK
.get_psk_info = _peer_get_psk_info_handler,
#endif /* CONFIG_DTLS_PSK */
#ifdef CONFIG_DTLS_ECC
.get_ecdsa_key = _peer_get_ecdsa_key_handler,
.verify_ecdsa_key = _peer_verify_ecdsa_key_handler
#endif /* CONFIG_DTLS_ECC */
};
#ifdef CONFIG_DTLS_PSK
DEBUG("Server support PSK\n");
#endif
#ifdef CONFIG_DTLS_ECC
DEBUG("Server support ECC\n");
#endif
#ifdef TINYDTLS_LOG_LVL
dtls_set_log_level(TINYDTLS_LOG_LVL);
#endif
/*
* 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.
*/
new_context = dtls_new_context(remote_peer);
if (new_context) {
dtls_set_handler(new_context, &cb);
}
else {
return NULL;
}
return new_context;
}
void *_dtls_server_wrapper(void *arg)
{
(void) arg;
bool active = true;
msg_t _reader_queue[READER_QUEUE_SIZE];
msg_t msg;
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);
/* NOTE: dtls_init() must be called previous to this (see main.c) */
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;
}
dtls_context = _server_init_dtls(&remote_peer);
if (!dtls_context) {
puts("ERROR: Server unable to load context!");
return (void *) NULL;
}
while (active) {
if ((msg_try_receive(&msg) == -1) && (msg.type == DTLS_STOP_SERVER_MSG)) {
active = false;
}
else {
/* Listening for any DTLS recodrd */
if (dtls_handle_read(dtls_context) < 0) {
printf("Received alert from client\n");
}
}
}
/* 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 */
return (void *) 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;
}
/* The server is initialized */
_dtls_server_pid = thread_create(_dtls_server_stack,
sizeof(_dtls_server_stack),
THREAD_PRIORITY_MAIN - 1,
0,
_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;
}
if (_dtls_server_pid == EOVERFLOW) {
puts("ERROR: Thread overflow!");
_dtls_server_pid = KERNEL_PID_UNDEF;
return;
}
return;
}
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;
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)
{
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 0;
}