mirror of
https://github.com/eclipse/tinydtls.git
synced 2025-05-08 11:27:36 +08:00

Simple client side implementation indicates support and uses the cid of the server, when negotiated by that. Signed-off-by: Achim Kraus <achim.kraus@cloudcoap.net>
783 lines
28 KiB
C
783 lines
28 KiB
C
/*******************************************************************************
|
|
*
|
|
* Copyright (c) 2011, 2012, 2013, 2014, 2015 Olaf Bergmann (TZI) and others.
|
|
* All rights reserved. This program and the accompanying materials
|
|
* are made available under the terms of the Eclipse Public License v1.0
|
|
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
|
|
*
|
|
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
|
|
* and the Eclipse Distribution License is available at
|
|
* http://www.eclipse.org/org/documents/edl-v10.php.
|
|
*
|
|
* Contributors:
|
|
* Olaf Bergmann - initial API and implementation
|
|
* Hauke Mehrtens - memory optimization, ECC integration
|
|
* Achim Kraus - session recovery
|
|
*
|
|
*******************************************************************************/
|
|
|
|
/**
|
|
* @file dtls.h
|
|
* @brief High level DTLS API and visible structures.
|
|
*/
|
|
|
|
#ifndef _DTLS_DTLS_H_
|
|
#define _DTLS_DTLS_H_
|
|
|
|
#include <stdint.h>
|
|
|
|
#include "tinydtls.h"
|
|
|
|
#include "state.h"
|
|
#include "peer.h"
|
|
|
|
#include "uthash.h"
|
|
|
|
#include "alert.h"
|
|
#include "crypto.h"
|
|
#include "hmac.h"
|
|
|
|
#include "global.h"
|
|
#include "dtls_time.h"
|
|
|
|
#ifndef DTLSv12
|
|
#define DTLS_VERSION 0xfeff /* DTLS v1.1 */
|
|
#else
|
|
#define DTLS_VERSION 0xfefd /* DTLS v1.2 */
|
|
#endif
|
|
|
|
typedef enum dtls_credentials_type_t {
|
|
DTLS_PSK_HINT, DTLS_PSK_IDENTITY, DTLS_PSK_KEY
|
|
} dtls_credentials_type_t;
|
|
|
|
typedef struct dtls_ecdsa_key_t {
|
|
dtls_ecdh_curve curve;
|
|
const unsigned char *priv_key; /** < private key as bytes > */
|
|
const unsigned char *pub_key_x; /** < x part of the public key for the given private key > */
|
|
const unsigned char *pub_key_y; /** < y part of the public key for the given private key > */
|
|
} dtls_ecdsa_key_t;
|
|
|
|
/** Length of the secret that is used for generating Hello Verify cookies. */
|
|
#define DTLS_COOKIE_SECRET_LENGTH 12
|
|
|
|
struct dtls_context_t;
|
|
|
|
/**
|
|
* This structure contains callback functions used by tinydtls to
|
|
* communicate with the application. At least the write function must
|
|
* be provided. It is called by the DTLS state machine to send packets
|
|
* over the network. The read function is invoked to deliver decrypted
|
|
* and verfified application data. The third callback is an event
|
|
* handler function that is called when alert messages are encountered
|
|
* or events generated by the library have occured.
|
|
*/
|
|
typedef struct {
|
|
/**
|
|
* Called from dtls_handle_message() to send DTLS packets over the
|
|
* network. The callback function must use the network interface
|
|
* denoted by session->ifindex to send the data.
|
|
*
|
|
* @param ctx The current DTLS context.
|
|
* @param session The session object, including the address of the
|
|
* remote peer where the data shall be sent.
|
|
* @param buf The data to send.
|
|
* @param len The actual length of @p buf.
|
|
* @return The callback function must return the number of bytes
|
|
* that were sent, or a value less than zero to indicate an
|
|
* error.
|
|
*/
|
|
int (*write)(struct dtls_context_t *ctx,
|
|
session_t *session, uint8 *buf, size_t len);
|
|
|
|
/**
|
|
* Called from dtls_handle_message() deliver application data that was
|
|
* received on the given session. The data is delivered only after
|
|
* decryption and verification have succeeded.
|
|
*
|
|
* @param ctx The current DTLS context.
|
|
* @param session The session object, including the address of the
|
|
* data's origin.
|
|
* @param buf The received data packet.
|
|
* @param len The actual length of @p buf.
|
|
* @return ignored
|
|
*/
|
|
int (*read)(struct dtls_context_t *ctx,
|
|
session_t *session, uint8 *buf, size_t len);
|
|
|
|
/**
|
|
* The event handler is called when a message from the alert
|
|
* protocol is received or the state of the DTLS session changes.
|
|
*
|
|
* @param ctx The current dtls context.
|
|
* @param session The session object that was affected.
|
|
* @param level The alert level or @c 0 when an event ocurred that
|
|
* is not an alert.
|
|
* @param code Values less than @c 256 indicate alerts, while
|
|
* @c 256 or greater indicate internal DTLS session changes.
|
|
* @return ignored
|
|
*/
|
|
int (*event)(struct dtls_context_t *ctx, session_t *session,
|
|
dtls_alert_level_t level, unsigned short code);
|
|
|
|
/**
|
|
* Called during handshake to get the user parameter.
|
|
*
|
|
* @param ctx The current dtls context.
|
|
* @param session The session where the cipher suites will be used.
|
|
* @param parameters The pointer to user parameters.
|
|
* The user parameters are initialized with the default
|
|
* values.
|
|
*/
|
|
void (*get_user_parameters)(struct dtls_context_t *ctx, session_t *session,
|
|
dtls_user_parameters_t *parameters);
|
|
|
|
#ifdef DTLS_PSK
|
|
/**
|
|
* Called during handshake to get information related to the
|
|
* psk key exchange. The type of information requested is
|
|
* indicated by @p type which will be one of DTLS_PSK_HINT,
|
|
* DTLS_PSK_IDENTITY, or DTLS_PSK_KEY. The called function
|
|
* must store the requested item in the buffer @p result of
|
|
* size @p result_length. On success, the function must return
|
|
* the actual number of bytes written to @p result, of a
|
|
* value less than zero on error. The parameter @p desc may
|
|
* contain additional request information (e.g. the psk_identity
|
|
* for which a key is requested when @p type == @c DTLS_PSK_KEY.
|
|
*
|
|
* @param ctx The current dtls context.
|
|
* @param session The session where the key will be used.
|
|
* @param type The type of the requested information.
|
|
* @param desc Additional request information
|
|
* @param desc_len The actual length of desc.
|
|
* @param result Must be filled with the requested information.
|
|
* @param result_length Maximum size of @p result.
|
|
* @return The number of bytes written to @p result or a value
|
|
* less than zero on error.
|
|
*/
|
|
int (*get_psk_info)(struct dtls_context_t *ctx,
|
|
const session_t *session,
|
|
dtls_credentials_type_t type,
|
|
const unsigned char *desc, size_t desc_len,
|
|
unsigned char *result, size_t result_length);
|
|
|
|
#endif /* DTLS_PSK */
|
|
|
|
#ifdef DTLS_ECC
|
|
/**
|
|
* Called during handshake to get the server's or client's ecdsa
|
|
* key used to authenticate this server or client in this
|
|
* session. If found, the key must be stored in @p result and
|
|
* the return value must be @c 0. If not found, @p result is
|
|
* undefined and the return value must be less than zero.
|
|
*
|
|
* If ECDSA should not be supported, set this pointer to NULL.
|
|
*
|
|
* Implement this if you want to provide your own certificate to
|
|
* the other peer. This is mandatory for a server providing ECDSA
|
|
* support and optional for a client. A client doing DTLS client
|
|
* authentication has to implementing this callback.
|
|
*
|
|
* @param ctx The current dtls context.
|
|
* @param session The session where the key will be used.
|
|
* @param result Must be set to the key object to used for the given
|
|
* session.
|
|
* @return @c 0 if result is set, or less than zero on error.
|
|
*/
|
|
int (*get_ecdsa_key)(struct dtls_context_t *ctx,
|
|
const session_t *session,
|
|
const dtls_ecdsa_key_t **result);
|
|
|
|
/**
|
|
* Called during handshake to check the peer's pubic key in this
|
|
* session. If the public key matches the session and should be
|
|
* considerated valid the return value must be @c 0. If not valid,
|
|
* the return value must be less than zero.
|
|
*
|
|
* If ECDSA should not be supported, set this pointer to NULL.
|
|
*
|
|
* Implement this if you want to verify the other peers public key.
|
|
* This is mandatory for a DTLS client doing based ECDSA
|
|
* authentication. A server implementing this will request the
|
|
* client to do DTLS client authentication.
|
|
*
|
|
* @param ctx The current dtls context.
|
|
* @param session The session where the key will be used.
|
|
* @param other_pub_x x component of the public key.
|
|
* @param other_pub_y y component of the public key.
|
|
* @return @c 0 if public key matches, or less than zero on error.
|
|
* error codes:
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_BAD_CERTIFICATE);
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_UNSUPPORTED_CERTIFICATE);
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_CERTIFICATE_REVOKED);
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_CERTIFICATE_EXPIRED);
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_CERTIFICATE_UNKNOWN);
|
|
* return dtls_alert_fatal_create(DTLS_ALERT_UNKNOWN_CA);
|
|
*/
|
|
int (*verify_ecdsa_key)(struct dtls_context_t *ctx,
|
|
const session_t *session,
|
|
const unsigned char *other_pub_x,
|
|
const unsigned char *other_pub_y,
|
|
size_t key_size);
|
|
#endif /* DTLS_ECC */
|
|
} dtls_handler_t;
|
|
|
|
struct netq_t;
|
|
|
|
/** Holds global information of the DTLS engine. */
|
|
typedef struct dtls_context_t {
|
|
unsigned char cookie_secret[DTLS_COOKIE_SECRET_LENGTH];
|
|
clock_time_t cookie_secret_age; /**< the time the secret has been generated */
|
|
|
|
dtls_peer_t *peers; /**< peer hash map */
|
|
#ifdef WITH_CONTIKI
|
|
struct etimer retransmit_timer; /**< fires when the next packet must be sent */
|
|
#endif /* WITH_CONTIKI */
|
|
|
|
struct netq_t *sendqueue; /**< the packets to send */
|
|
|
|
void *app; /**< application-specific data */
|
|
|
|
dtls_handler_t *h; /**< callback handlers */
|
|
} dtls_context_t;
|
|
|
|
/**
|
|
* This function initializes the tinyDTLS memory management and must
|
|
* be called first.
|
|
*/
|
|
void dtls_init(void);
|
|
|
|
/**
|
|
* Creates a new context object. The storage allocated for the new
|
|
* object must be released with dtls_free_context(). */
|
|
dtls_context_t *dtls_new_context(void *app_data);
|
|
|
|
/** Releases any storage that has been allocated for \p ctx. */
|
|
void dtls_free_context(dtls_context_t *ctx);
|
|
|
|
#define dtls_set_app_data(CTX,DATA) ((CTX)->app = (DATA))
|
|
#define dtls_get_app_data(CTX) ((CTX)->app)
|
|
|
|
/** Sets the callback handler object for @p ctx to @p h. */
|
|
static inline void dtls_set_handler(dtls_context_t *ctx, dtls_handler_t *h) {
|
|
ctx->h = h;
|
|
}
|
|
|
|
/**
|
|
* Establishes a DTLS channel with the specified remote peer @p dst.
|
|
* This function returns @c 0 if that channel already exists, a value
|
|
* greater than zero when a new ClientHello message was sent, and
|
|
* a value less than zero on error.
|
|
*
|
|
* @param ctx The DTLS context to use.
|
|
* @param dst The remote party to connect to.
|
|
* @return A value less than zero on error, greater or equal otherwise.
|
|
*/
|
|
int dtls_connect(dtls_context_t *ctx, const session_t *dst);
|
|
|
|
/**
|
|
* Establishes a DTLS channel with the specified remote peer.
|
|
* This function returns @c 0 if that channel already exists and a renegotiate
|
|
* was initiated, a value greater than zero when a new ClientHello message was
|
|
* sent, and a value less than zero on error.
|
|
*
|
|
* @param ctx The DTLS context to use.
|
|
* @param peer The peer object that describes the session.
|
|
* @return A value less than zero on error, greater or equal otherwise.
|
|
*/
|
|
int dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer);
|
|
|
|
/**
|
|
* Closes the DTLS connection associated with @p remote. This function
|
|
* returns zero on success, and a value less than zero on error.
|
|
*/
|
|
int dtls_close(dtls_context_t *ctx, const session_t *remote);
|
|
|
|
/**
|
|
* Writes the application data given in multiple buffers to the peer
|
|
* specified by @p session.
|
|
*
|
|
* @param ctx The DTLS context to use.
|
|
* @param session The remote transport address and local interface.
|
|
* @param buf_array Array of buffers with the data to write.
|
|
* @param buf_len_array The length of the arrays in @p buf_array.
|
|
* @param buf_array_len The number of data arrays.
|
|
*
|
|
* @return The number of bytes written, @c -1 on error or @c 0
|
|
* if the peer already exists but is not connected yet.
|
|
*/
|
|
int dtls_writev(struct dtls_context_t *ctx,
|
|
session_t *session, uint8 *buf_array[],
|
|
size_t buf_len_array[], size_t buf_array_len);
|
|
|
|
/**
|
|
* Writes the application data given in @p buf to the peer specified
|
|
* by @p session.
|
|
*
|
|
* @param ctx The DTLS context to use.
|
|
* @param session The remote transport address and local interface.
|
|
* @param buf The data to write.
|
|
* @param len The actual length of @p data.
|
|
*
|
|
* @return The number of bytes written, @c -1 on error or @c 0
|
|
* if the peer already exists but is not connected yet.
|
|
*/
|
|
int dtls_write(struct dtls_context_t *ctx, session_t *session,
|
|
uint8 *buf, size_t len);
|
|
|
|
/**
|
|
* Checks sendqueue of given DTLS context object for any outstanding
|
|
* packets to be transmitted.
|
|
*
|
|
* @param context The DTLS context object to use.
|
|
* @param next If not NULL, @p next is filled with the timestamp
|
|
* of the next scheduled retransmission, or @c 0 when no packets are
|
|
* waiting.
|
|
*/
|
|
void dtls_check_retransmit(dtls_context_t *context, clock_time_t *next);
|
|
|
|
#define DTLS_COOKIE_LENGTH 16
|
|
|
|
#define DTLS_CT_CHANGE_CIPHER_SPEC 20
|
|
#define DTLS_CT_ALERT 21
|
|
#define DTLS_CT_HANDSHAKE 22
|
|
#define DTLS_CT_APPLICATION_DATA 23
|
|
#define DTLS_CT_TLS12_CID 25
|
|
|
|
#ifdef __GNUC__
|
|
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
|
|
#elif defined(_MSC_VER)
|
|
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
|
|
#else
|
|
#error "Structure packing is not available for the used compiler."
|
|
#endif
|
|
|
|
/** Generic header structure of the DTLS record layer. */
|
|
PACK(typedef struct) {
|
|
uint8 content_type; /**< content type of the included message */
|
|
uint16 version; /**< Protocol version */
|
|
uint16 epoch; /**< counter for cipher state changes */
|
|
uint48 sequence_number; /**< sequence number */
|
|
uint16 length; /**< length of the following fragment */
|
|
/* fragment */
|
|
} dtls_record_header_t;
|
|
|
|
/* Handshake types */
|
|
|
|
#define DTLS_HT_HELLO_REQUEST 0
|
|
#define DTLS_HT_CLIENT_HELLO 1
|
|
#define DTLS_HT_SERVER_HELLO 2
|
|
#define DTLS_HT_HELLO_VERIFY_REQUEST 3
|
|
#define DTLS_HT_CERTIFICATE 11
|
|
#define DTLS_HT_SERVER_KEY_EXCHANGE 12
|
|
#define DTLS_HT_CERTIFICATE_REQUEST 13
|
|
#define DTLS_HT_SERVER_HELLO_DONE 14
|
|
#define DTLS_HT_CERTIFICATE_VERIFY 15
|
|
#define DTLS_HT_CLIENT_KEY_EXCHANGE 16
|
|
#define DTLS_HT_FINISHED 20
|
|
|
|
/**
|
|
* Pseudo handshake message type, if no optional handshake message is expected.
|
|
*/
|
|
#define DTLS_HT_NO_OPTIONAL_MESSAGE -1
|
|
|
|
/** Header structure for the DTLS handshake protocol. */
|
|
PACK(typedef struct) {
|
|
uint8 msg_type; /**< Type of handshake message (one of DTLS_HT_) */
|
|
uint24 length; /**< length of this message */
|
|
uint16 message_seq; /**< Message sequence number */
|
|
uint24 fragment_offset; /**< Fragment offset. */
|
|
uint24 fragment_length; /**< Fragment length. */
|
|
/* body */
|
|
} dtls_handshake_header_t;
|
|
|
|
/** Structure of the Client Hello message. */
|
|
PACK(typedef struct) {
|
|
uint16 version; /**< Client version */
|
|
uint32 gmt_random; /**< GMT time of the random byte creation */
|
|
unsigned char random[28]; /**< Client random bytes */
|
|
/* session id (up to 32 bytes) */
|
|
/* cookie (up to 32 bytes) */
|
|
/* cipher suite (2 to 2^16 -1 bytes) */
|
|
/* compression method */
|
|
} dtls_client_hello_t;
|
|
|
|
/** Structure of the Hello Verify Request. */
|
|
PACK(typedef struct) {
|
|
uint16 version; /**< Server version */
|
|
uint8 cookie_length; /**< Length of the included cookie */
|
|
uint8 cookie[]; /**< up to 32 bytes making up the cookie */
|
|
} dtls_hello_verify_t;
|
|
|
|
#if 0
|
|
/**
|
|
* Checks a received DTLS record for consistency and eventually decrypt,
|
|
* verify, decompress and reassemble the contained fragment for
|
|
* delivery to high-lever clients.
|
|
*
|
|
* \param state The DTLS record state for the current session.
|
|
* \param
|
|
*/
|
|
int dtls_record_read(dtls_state_t *state, uint8 *msg, int msglen);
|
|
#endif
|
|
|
|
/**
|
|
* Handles incoming data as DTLS message from given peer.
|
|
*
|
|
* @param ctx The dtls context to use.
|
|
* @param session The current session
|
|
* @param msg The received data
|
|
* @param msglen The actual length of @p msg.
|
|
* @return A value less than zero on error, zero on success.
|
|
*/
|
|
int dtls_handle_message(dtls_context_t *ctx, session_t *session,
|
|
uint8 *msg, int msglen);
|
|
|
|
/**
|
|
* Check if @p session is associated with a peer object in @p context.
|
|
* This function returns a pointer to the peer if found, NULL otherwise.
|
|
*
|
|
* @param context The DTLS context to search.
|
|
* @param session The remote address and local interface
|
|
* @return A pointer to the peer associated with @p session or NULL if
|
|
* none exists.
|
|
*/
|
|
dtls_peer_t *dtls_get_peer(const dtls_context_t *context,
|
|
const session_t *session);
|
|
|
|
/**
|
|
* Resets all connections with @p peer.
|
|
*
|
|
* @param context The active DTLS context.
|
|
* @param peer The peer to reset.
|
|
*/
|
|
void dtls_reset_peer(dtls_context_t *context, dtls_peer_t *peer);
|
|
|
|
#endif /* _DTLS_DTLS_H_ */
|
|
|
|
/**
|
|
* @mainpage
|
|
*
|
|
* @author Olaf Bergmann, TZI Uni Bremen
|
|
*
|
|
* This library provides a very simple datagram server with DTLS
|
|
* support. It is designed to support session multiplexing in
|
|
* single-threaded applications and thus targets specifically on
|
|
* embedded systems.
|
|
*
|
|
* @section license License
|
|
*
|
|
* This software is under the <a
|
|
* href="http://www.opensource.org/licenses/mit-license.php">MIT License</a>.
|
|
*
|
|
* @subsection uthash UTHash
|
|
*
|
|
* This library uses <a href="http://uthash.sourceforge.net/">uthash</a> to manage
|
|
* its peers (not used for Contiki). @b uthash uses the <b>BSD revised license</b>, see
|
|
* <a href="http://uthash.sourceforge.net/license.html">http://uthash.sourceforge.net/license.html</a>.
|
|
*
|
|
* @subsection sha256 Aaron D. Gifford's SHA256 Implementation
|
|
*
|
|
* tinyDTLS provides HMAC-SHA256 with BSD-licensed code from Aaron D. Gifford,
|
|
* see <a href="http://www.aarongifford.com/">www.aarongifford.com</a>.
|
|
*
|
|
* @subsection aes Rijndael Implementation From OpenBSD
|
|
*
|
|
* The AES implementation is taken from rijndael.{c,h} contained in the crypto
|
|
* sub-system of the OpenBSD operating system. It is copyright by Vincent Rijmen, *
|
|
* Antoon Bosselaers and Paulo Barreto. See <a
|
|
* href="http://www.openbsd.org/cgi-bin/cvsweb/src/sys/crypto/rijndael.c">rijndael.c</a>
|
|
* for License info.
|
|
*
|
|
* @section download Getting the Files
|
|
*
|
|
* You can get the sources either from the <a
|
|
* href="http://sourceforge.net/projects/tinydtls/files">downloads</a> section or
|
|
* through git from the <a
|
|
* href="http://sourceforge.net/projects/tinydtls/develop">project develop page</a>.
|
|
*
|
|
* @section config Configuration
|
|
*
|
|
* Use @c configure to set up everything for a successful build. For Contiki, use the
|
|
* option @c --with-contiki.
|
|
*
|
|
* @section build Building
|
|
*
|
|
* After configuration, just type
|
|
* @code
|
|
make
|
|
* @endcode
|
|
* optionally followed by
|
|
* @code
|
|
make install
|
|
* @endcode
|
|
* The Contiki version is integrated with the Contiki build system, hence you do not
|
|
* need to invoke @c make explicitely. Just add @c tinydtls to the variable @c APPS
|
|
* in your @c Makefile.
|
|
*
|
|
* @addtogroup dtls_usage DTLS Usage
|
|
*
|
|
* @section dtls_server_example DTLS Server Example
|
|
*
|
|
* This section shows how to use the DTLS library functions to setup a
|
|
* simple secure UDP echo server. The application is responsible for the
|
|
* entire network communication and thus will look like a usual UDP
|
|
* server with socket creation and binding and a typical select-loop as
|
|
* shown below. The minimum configuration required for DTLS is the
|
|
* creation of the dtls_context_t using dtls_new_context(), and a callback
|
|
* for sending data. Received packets are read by the application and
|
|
* passed to dtls_handle_message() as shown in @ref dtls_read_cb.
|
|
* For any useful communication to happen, read and write call backs
|
|
* and a key management function should be registered as well.
|
|
*
|
|
* @code
|
|
dtls_context_t *the_context = NULL;
|
|
int fd, result;
|
|
|
|
static dtls_handler_t cb = {
|
|
.write = send_to_peer,
|
|
.read = read_from_peer,
|
|
.event = NULL,
|
|
.get_psk_key = get_psk_key
|
|
};
|
|
|
|
fd = socket(...);
|
|
if (fd < 0 || bind(fd, ...) < 0)
|
|
exit(-1);
|
|
|
|
the_context = dtls_new_context(&fd);
|
|
dtls_set_handler(the_context, &cb);
|
|
|
|
while (1) {
|
|
...initialize fd_set rfds and timeout ...
|
|
result = select(fd+1, &rfds, NULL, 0, NULL);
|
|
|
|
if (FD_ISSET(fd, &rfds))
|
|
dtls_handle_read(the_context);
|
|
}
|
|
|
|
dtls_free_context(the_context);
|
|
* @endcode
|
|
*
|
|
* @subsection dtls_read_cb The Read Callback
|
|
*
|
|
* The DTLS library expects received raw data to be passed to
|
|
* dtls_handle_message(). The application is responsible for
|
|
* filling a session_t structure with the address data of the
|
|
* remote peer as illustrated by the following example:
|
|
*
|
|
* @code
|
|
int dtls_handle_read(struct dtls_context_t *ctx) {
|
|
int *fd;
|
|
session_t session;
|
|
static uint8 buf[DTLS_MAX_BUF];
|
|
int len;
|
|
|
|
fd = dtls_get_app_data(ctx);
|
|
|
|
assert(fd);
|
|
|
|
session.size = sizeof(session.addr);
|
|
len = recvfrom(*fd, buf, sizeof(buf), 0, &session.addr.sa, &session.size);
|
|
|
|
return len < 0 ? len : dtls_handle_message(ctx, &session, buf, len);
|
|
}
|
|
* @endcode
|
|
*
|
|
* Once a new DTLS session was established and DTLS ApplicationData has been
|
|
* received, the DTLS server invokes the read callback with the MAC-verified
|
|
* cleartext data as its argument. A read callback for a simple echo server
|
|
* could look like this:
|
|
* @code
|
|
int read_from_peer(struct dtls_context_t *ctx, session_t *session, uint8 *data, size_t len) {
|
|
return dtls_write(ctx, session, data, len);
|
|
}
|
|
* @endcode
|
|
*
|
|
* @subsection dtls_send_cb The Send Callback
|
|
*
|
|
* The callback function send_to_peer() is called whenever data must be
|
|
* sent over the network. Here, the sendto() system call is used to
|
|
* transmit data within the given session. The socket descriptor required
|
|
* by sendto() has been registered as application data when the DTLS context
|
|
* was created with dtls_new_context().
|
|
* Note that it is on the application to buffer the data when it cannot be
|
|
* sent at the time this callback is invoked. The following example thus
|
|
* is incomplete as it would have to deal with EAGAIN somehow.
|
|
* @code
|
|
int send_to_peer(struct dtls_context_t *ctx, session_t *session, uint8 *data, size_t len) {
|
|
int fd = *(int *)dtls_get_app_data(ctx);
|
|
return sendto(fd, data, len, MSG_DONTWAIT, &session->addr.sa, session->size);
|
|
}
|
|
* @endcode
|
|
*
|
|
* @subsection dtls_get_psk_info The Key Storage
|
|
*
|
|
* When a new DTLS session is created, the library must ask the application
|
|
* for keying material. To do so, it invokes the registered call-back function
|
|
* get_psk_info() with the current context and session information as parameter.
|
|
* When the call-back function is invoked with the parameter @p type set to
|
|
* @c DTLS_PSK_IDENTITY, the result parameter @p result must be filled with
|
|
* the psk_identity_hint in case of a server, or the actual psk_identity in
|
|
* case of a client. When @p type is @c DTLS_PSK_KEY, the result parameter
|
|
* must be filled with a key for the given identity @p id. The function must
|
|
* return the number of bytes written to @p result which must not exceed
|
|
* @p result_length.
|
|
* In case of an error, the function must return a negative value that
|
|
* corresponds to a valid error code defined in alert.h.
|
|
*
|
|
* @code
|
|
int 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:
|
|
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);
|
|
}
|
|
* @endcode
|
|
*
|
|
* @subsection dtls_events The Event Notifier
|
|
*
|
|
* Applications that want to be notified whenever the status of a DTLS session
|
|
* has changed can register an event handling function with the field @c event
|
|
* in the dtls_handler_t structure (see \ref dtls_server_example). The call-back
|
|
* function is called for alert messages and internal state changes. For alert
|
|
* messages, the argument @p level will be set to a value greater than zero, and
|
|
* @p code will indicate the notification code. For internal events, @p level
|
|
* is @c 0, and @p code a value greater than @c 255.
|
|
*
|
|
* Internal events are DTLS_EVENT_CONNECTED, @c DTLS_EVENT_CONNECT, and
|
|
* @c DTLS_EVENT_RENEGOTIATE.
|
|
*
|
|
* @code
|
|
int handle_event(struct dtls_context_t *ctx, session_t *session,
|
|
dtls_alert_level_t level, unsigned short code) {
|
|
... do something with event ...
|
|
return 0;
|
|
}
|
|
* @endcode
|
|
*
|
|
* @section dtls_client_example DTLS Client Example
|
|
*
|
|
* A DTLS client is constructed like a server but needs to actively setup
|
|
* a new session by calling dtls_connect() at some point. As this function
|
|
* usually returns before the new DTLS channel is established, the application
|
|
* must register an event handler and wait for @c DTLS_EVENT_CONNECT before
|
|
* it can send data over the DTLS channel.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @addtogroup contiki Contiki
|
|
*
|
|
* To use tinyDTLS as Contiki application, place the source code in the directory
|
|
* @c apps/tinydtls in the Contiki source tree and invoke configure with the option
|
|
* @c --with-contiki. This will define WITH_CONTIKI in tinydtls.h and include
|
|
* @c Makefile.contiki in the main Makefile. To cross-compile for another platform
|
|
* you will need to set your host and build system accordingly. For example,
|
|
* when configuring for ARM, you would invoke
|
|
* @code
|
|
./configure --with-contiki --build=x86_64-linux-gnu --host=arm-none-eabi
|
|
* @endcode
|
|
* on an x86_64 linux host.
|
|
*
|
|
* Then, create a Contiki project with @c APPS += tinydtls in its Makefile. A sample
|
|
* server could look like this (with read_from_peer() and get_psk_key() as shown above).
|
|
*
|
|
* @code
|
|
#include "contiki.h"
|
|
|
|
#include "tinydtls.h"
|
|
#include "dtls.h"
|
|
|
|
#define UIP_IP_BUF ((struct uip_ip_hdr *)&uip_buf[UIP_LLH_LEN])
|
|
#define UIP_UDP_BUF ((struct uip_udp_hdr *)&uip_buf[UIP_LLIPH_LEN])
|
|
|
|
int send_to_peer(struct dtls_context_t *, session_t *, uint8 *, size_t);
|
|
|
|
static struct uip_udp_conn *server_conn;
|
|
static dtls_context_t *dtls_context;
|
|
|
|
static dtls_handler_t cb = {
|
|
.write = send_to_peer,
|
|
.read = read_from_peer,
|
|
.event = NULL,
|
|
.get_psk_key = get_psk_key
|
|
};
|
|
|
|
PROCESS(server_process, "DTLS server process");
|
|
AUTOSTART_PROCESSES(&server_process);
|
|
|
|
PROCESS_THREAD(server_process, ev, data)
|
|
{
|
|
PROCESS_BEGIN();
|
|
|
|
dtls_init();
|
|
|
|
server_conn = udp_new(NULL, 0, NULL);
|
|
udp_bind(server_conn, UIP_HTONS(5684));
|
|
|
|
dtls_context = dtls_new_context(server_conn);
|
|
if (!dtls_context) {
|
|
dtls_emerg("cannot create context\n");
|
|
PROCESS_EXIT();
|
|
}
|
|
|
|
dtls_set_handler(dtls_context, &cb);
|
|
|
|
while(1) {
|
|
PROCESS_WAIT_EVENT();
|
|
if(ev == tcpip_event && uip_newdata()) {
|
|
session_t session;
|
|
|
|
uip_ipaddr_copy(&session.addr, &UIP_IP_BUF->srcipaddr);
|
|
session.port = UIP_UDP_BUF->srcport;
|
|
session.size = sizeof(session.addr) + sizeof(session.port);
|
|
|
|
dtls_handle_message(ctx, &session, uip_appdata, uip_datalen());
|
|
}
|
|
}
|
|
|
|
PROCESS_END();
|
|
}
|
|
|
|
int send_to_peer(struct dtls_context_t *ctx, session_t *session, uint8 *data, size_t len) {
|
|
struct uip_udp_conn *conn = (struct uip_udp_conn *)dtls_get_app_data(ctx);
|
|
|
|
uip_ipaddr_copy(&conn->ripaddr, &session->addr);
|
|
conn->rport = session->port;
|
|
|
|
uip_udp_packet_send(conn, data, len);
|
|
|
|
memset(&conn->ripaddr, 0, sizeof(server_conn->ripaddr));
|
|
memset(&conn->rport, 0, sizeof(conn->rport));
|
|
|
|
return len;
|
|
}
|
|
* @endcode
|
|
*/
|