Version 0.5.0 of tinydtls

Dependents:   tinydtls_test_cellular tinydtls_test_ethernet tiny-dtls

Revision:
0:ff9ebe0cf0e9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dtls.c	Fri Oct 18 13:18:30 2013 +0000
@@ -0,0 +1,3716 @@
+/* dtls -- a very basic DTLS implementation
+ *
+ * Copyright (C) 2011--2012 Olaf Bergmann <bergmann@tzi.org>
+ * Copyright (C) 2013 Hauke Mehrtens <hauke@hauke-m.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "config.h"
+#include "dtls_time.h"
+
+#define __DEBUG__ 0
+#ifndef __MODULE__
+#define __MODULE__ "dtls.c"
+#endif
+
+#include "dbg.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#ifdef HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#ifndef WITH_CONTIKI
+#include <stdlib.h>
+#include "uthash.h"
+#else /* WITH_CONTIKI */
+# ifndef NDEBUG
+#   define DEBUG DEBUG_PRINT
+#   include "net/uip-debug.h"
+#  endif /* NDEBUG */
+#endif /* WITH_CONTIKI */
+
+#include "debug.h"
+#include "numeric.h"
+#include "netq.h"
+#include "dtls.h"
+
+#ifdef WITH_SHA256
+#  include "sha2/sha2.h"
+#endif
+
+#define dtls_set_version(H,V) dtls_int_to_uint16(&(H)->version, (V))
+#define dtls_set_content_type(H,V) ((H)->content_type = (V) & 0xff)
+#define dtls_set_length(H,V)  ((H)->length = (V))
+
+#define dtls_get_content_type(H) ((H)->content_type & 0xff)
+#define dtls_get_version(H) dtls_uint16_to_int(&(H)->version)
+#define dtls_get_epoch(H) dtls_uint16_to_int(&(H)->epoch)
+#define dtls_get_sequence_number(H) dtls_uint48_to_ulong(&(H)->sequence_number)
+#define dtls_get_fragment_length(H) dtls_uint24_to_int(&(H)->fragment_length)
+
+#ifndef WITH_CONTIKI
+#define HASH_FIND_PEER(head,sess,out)		\
+  HASH_FIND(hh,head,sess,sizeof(session_t),out)
+#define HASH_ADD_PEER(head,sess,add)		\
+  HASH_ADD(hh,head,sess,sizeof(session_t),add)
+#define HASH_DEL_PEER(head,delptr)		\
+  HASH_DELETE(hh,head,delptr)
+#endif /* WITH_CONTIKI */
+
+#define DTLS_RH_LENGTH sizeof(dtls_record_header_t)
+#define DTLS_HS_LENGTH sizeof(dtls_handshake_header_t)
+#define DTLS_CH_LENGTH sizeof(dtls_client_hello_t) /* no variable length fields! */
+#define DTLS_COOKIE_LENGTH_MAX 32
+#define DTLS_CH_LENGTH_MAX sizeof(dtls_client_hello_t) + DTLS_COOKIE_LENGTH_MAX + 12 + 26
+#define DTLS_HV_LENGTH sizeof(dtls_hello_verify_t)
+#define DTLS_SH_LENGTH (2 + DTLS_RANDOM_LENGTH + 1 + 2 + 1)
+#define DTLS_CE_LENGTH (3 + 3 + 27 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE)
+#define DTLS_SKEXEC_LENGTH (1 + 2 + 1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE + 2 + 70)
+#define DTLS_SKEXECPSK_LENGTH_MIN 2
+#define DTLS_SKEXECPSK_LENGTH_MAX 2 + DTLS_PSK_MAX_CLIENT_IDENTITY_LEN
+#define DTLS_CKXPSK_LENGTH_MIN 2
+#define DTLS_CKXEC_LENGTH (1 + 1 + DTLS_EC_KEY_SIZE + DTLS_EC_KEY_SIZE)
+#define DTLS_CV_LENGTH (1 + 1 + 2 + 1 + 1 + 1 + 1 + DTLS_EC_KEY_SIZE + 1 + 1 + DTLS_EC_KEY_SIZE)
+#define DTLS_FIN_LENGTH 12
+
+#define HS_HDR_LENGTH  DTLS_RH_LENGTH + DTLS_HS_LENGTH
+#define HV_HDR_LENGTH  HS_HDR_LENGTH + DTLS_HV_LENGTH
+
+#define HIGH(V) (((V) >> 8) & 0xff)
+#define LOW(V)  ((V) & 0xff)
+
+#define DTLS_RECORD_HEADER(M) ((dtls_record_header_t *)(M))
+#define DTLS_HANDSHAKE_HEADER(M) ((dtls_handshake_header_t *)(M))
+
+#define HANDSHAKE(M) ((dtls_handshake_header_t *)((M) + DTLS_RH_LENGTH))
+#define CLIENTHELLO(M) ((dtls_client_hello_t *)((M) + HS_HDR_LENGTH))
+
+/* The length check here should work because dtls_*_to_int() works on
+ * unsigned char. Otherwise, broken messages could cause severe
+ * trouble. Note that this macro jumps out of the current program flow
+ * when the message is too short. Beware!
+ */
+#define SKIP_VAR_FIELD(P,L,T) {						\
+    if (L < dtls_ ## T ## _to_int(P) + sizeof(T))			\
+      goto error;							\
+    L -= dtls_ ## T ## _to_int(P) + sizeof(T);				\
+    P += dtls_ ## T ## _to_int(P) + sizeof(T);				\
+  }
+
+/* some constants for the PRF */
+#define PRF_LABEL(Label) prf_label_##Label
+#define PRF_LABEL_SIZE(Label) (sizeof(PRF_LABEL(Label)) - 1)
+
+static const unsigned char prf_label_master[] = "master secret";
+static const unsigned char prf_label_key[] = "key expansion";
+static const unsigned char prf_label_client[] = "client";
+static const unsigned char prf_label_server[] = "server";
+static const unsigned char prf_label_finished[] = " finished";
+
+/* first part of Raw public key, the is the start of the Subject Public Key */
+static const unsigned char cert_asn1_header[] = {
+  0x30, 0x59, /* SEQUENCE, length 89 bytes */
+    0x30, 0x13, /* SEQUENCE, length 19 bytes */
+      0x06, 0x07, /* OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1) */
+        0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
+      0x06, 0x08, /* OBJECT IDENTIFIER prime256v1 (1 2 840 10045 3 1 7) */
+        0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
+      0x03, 0x42, 0x00, /* BIT STRING, length 66 bytes, 0 bits unused */
+         0x04 /* uncompressed, followed by the r und s values of the public key */
+};
+
+static dtls_context_t the_dtls_context;
+
+#ifdef WITH_CONTIKI
+PROCESS(dtls_retransmit_process, "DTLS retransmit process");
+#endif
+
+void
+dtls_init() {
+  dtls_clock_init();
+  netq_init();
+  crypto_init();
+  peer_init();
+}
+
+/* Calls cb_alert() with given arguments if defined, otherwise an
+ * error message is logged and the result is -1. This is just an
+ * internal helper.
+ */
+#define CALL(Context, which, ...)					\
+  ((Context)->h && (Context)->h->which					\
+   ? (Context)->h->which((Context), ##__VA_ARGS__)			\
+   : -1)
+
+/** 
+ * Sends the fragment of length \p buflen given in \p buf to the
+ * specified \p peer. The data will be MAC-protected and encrypted
+ * according to the selected cipher and split into one or more DTLS
+ * records of the specified \p type. This function returns the number
+ * of bytes that were sent, or \c -1 if an error occurred.
+ *
+ * \param ctx    The DTLS context to use.
+ * \param peer   The remote peer.
+ * \param type   The content type of the record. 
+ * \param buf    The data to send.
+ * \param buflen The actual length of \p buf.
+ * \return Less than zero on error, the number of bytes written otherwise.
+ */
+static int dtls_send(dtls_context_t *ctx, dtls_peer_t *peer, unsigned char type,
+	      uint8 *buf, size_t buflen);
+
+/**
+ * Stops ongoing retransmissions of handshake messages for @p peer.
+ */
+static void dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer);
+
+dtls_peer_t *
+dtls_get_peer(const dtls_context_t *ctx, const session_t *session) {
+  dtls_peer_t *p = NULL;
+
+#ifndef WITH_CONTIKI
+  HASH_FIND_PEER(ctx->peers, session, p);
+#else /* WITH_CONTIKI */
+  for (p = list_head(ctx->peers); p; p = list_item_next(p))
+    if (dtls_session_equals(&p->session, session))
+      return p;
+#endif /* WITH_CONTIKI */
+  
+  return p;
+}
+
+static void
+dtls_add_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
+#ifndef WITH_CONTIKI
+  HASH_ADD_PEER(ctx->peers, session, peer);
+#else /* WITH_CONTIKI */
+  list_add(ctx->peers, peer);
+#endif /* WITH_CONTIKI */
+}
+
+int
+dtls_write(struct dtls_context_t *ctx, 
+	   session_t *dst, uint8 *buf, size_t len) {
+  
+  dtls_peer_t *peer = dtls_get_peer(ctx, dst);
+
+  /* Check if peer connection already exists */
+  if (!peer) { /* no ==> create one */
+    int res;
+
+    /* dtls_connect() returns a value greater than zero if a new
+     * connection attempt is made, 0 for session reuse. */
+    res = dtls_connect(ctx, dst);
+
+    return (res >= 0) ? 0 : res;
+  } else { /* a session exists, check if it is in state connected */
+    
+    if (peer->state != DTLS_STATE_CONNECTED) {
+      return 0;
+    } else {
+      return dtls_send(ctx, peer, DTLS_CT_APPLICATION_DATA, buf, len);
+    }
+  }
+}
+
+static int
+dtls_get_cookie(uint8 *msg, int msglen, uint8 **cookie) {
+  /* To access the cookie, we have to determine the session id's
+   * length and skip the whole thing. */
+  if (msglen < DTLS_HS_LENGTH + DTLS_CH_LENGTH + sizeof(uint8))
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+
+  if (dtls_uint16_to_int(msg + DTLS_HS_LENGTH) != DTLS_VERSION)
+    return dtls_alert_fatal_create(DTLS_ALERT_PROTOCOL_VERSION);
+
+  msglen -= DTLS_HS_LENGTH + DTLS_CH_LENGTH;
+  msg += DTLS_HS_LENGTH + DTLS_CH_LENGTH;
+
+  SKIP_VAR_FIELD(msg, msglen, uint8); /* skip session id */
+
+  if (msglen < (*msg & 0xff) + sizeof(uint8))
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  
+  *cookie = msg + sizeof(uint8);
+  return dtls_uint8_to_int(msg);
+
+ error:
+  return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+}
+
+static int
+dtls_create_cookie(dtls_context_t *ctx, 
+		   session_t *session,
+		   uint8 *msg, int msglen,
+		   uint8 *cookie, int *clen) {
+  unsigned char buf[DTLS_HMAC_MAX];
+  size_t len, e;
+
+  /* create cookie with HMAC-SHA256 over:
+   * - SECRET
+   * - session parameters (only IP address?)
+   * - client version 
+   * - random gmt and bytes
+   * - session id
+   * - cipher_suites 
+   * - compression method
+   */
+
+  /* We use our own buffer as hmac_context instead of a dynamic buffer
+   * created by dtls_hmac_new() to separate storage space for cookie
+   * creation from storage that is used in real sessions. Note that
+   * the buffer size must fit with the default hash algorithm (see
+   * implementation of dtls_hmac_context_new()). */
+
+  dtls_hmac_context_t hmac_context;
+  dtls_hmac_init(&hmac_context, ctx->cookie_secret, DTLS_COOKIE_SECRET_LENGTH);
+
+  dtls_hmac_update(&hmac_context, 
+		   (unsigned char *)&session->addr, session->size);
+
+  /* feed in the beginning of the Client Hello up to and including the
+     session id */
+  e = sizeof(dtls_client_hello_t);
+  e += (*(msg + DTLS_HS_LENGTH + e) & 0xff) + sizeof(uint8);
+  if (e + DTLS_HS_LENGTH > msglen)
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+
+  dtls_hmac_update(&hmac_context, msg + DTLS_HS_LENGTH, e);
+  
+  /* skip cookie bytes and length byte */
+  e += *(uint8 *)(msg + DTLS_HS_LENGTH + e) & 0xff;
+  e += sizeof(uint8);
+  if (e + DTLS_HS_LENGTH > msglen)
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+
+  dtls_hmac_update(&hmac_context, 
+		   msg + DTLS_HS_LENGTH + e,
+		   dtls_get_fragment_length(DTLS_HANDSHAKE_HEADER(msg)) - e);
+
+  len = dtls_hmac_finalize(&hmac_context, buf);
+
+  if (len < *clen) {
+    memset(cookie + len, 0, *clen - len);
+    *clen = len;
+  }
+  
+  memcpy(cookie, buf, *clen);
+  return 0;
+}
+
+#ifdef DTLS_CHECK_CONTENTTYPE
+/* used to check if a received datagram contains a DTLS message */
+static char const content_types[] = { 
+  DTLS_CT_CHANGE_CIPHER_SPEC,
+  DTLS_CT_ALERT,
+  DTLS_CT_HANDSHAKE,
+  DTLS_CT_APPLICATION_DATA,
+  0 				/* end marker */
+};
+#endif
+
+/**
+ * Checks if \p msg points to a valid DTLS record. If
+ * 
+ */
+static unsigned int
+is_record(uint8 *msg, int msglen) {
+  DBG("is_record");
+  unsigned int rlen = 0;
+
+  if (msglen >= DTLS_RH_LENGTH	/* FIXME allow empty records? */
+#ifdef DTLS_CHECK_CONTENTTYPE
+      && strchr(content_types, msg[0])
+#endif
+      && msg[1] == HIGH(DTLS_VERSION)
+      && msg[2] == LOW(DTLS_VERSION)) 
+    {
+      rlen = DTLS_RH_LENGTH + 
+	dtls_uint16_to_int(DTLS_RECORD_HEADER(msg)->length);
+      
+      /* we do not accept wrong length field in record header */
+      if (rlen > msglen)	
+	rlen = 0;
+  }
+  return rlen;
+}
+
+/**
+ * Initializes \p buf as record header. The caller must ensure that \p
+ * buf is capable of holding at least \c sizeof(dtls_record_header_t)
+ * bytes. Increments sequence number counter of \p peer.
+ * \return pointer to the next byte after the written header
+ */ 
+static inline uint8 *
+dtls_set_record_header(uint8 type, dtls_peer_t *peer, uint8 *buf) {
+  
+  dtls_int_to_uint8(buf, type);
+  buf += sizeof(uint8);
+
+  dtls_int_to_uint16(buf, DTLS_VERSION);
+  buf += sizeof(uint16);
+
+  if (peer) {
+    memcpy(buf, &peer->epoch, sizeof(uint16) + sizeof(uint48));
+
+    /* increment record sequence counter by 1 */
+    inc_uint(uint48, peer->rseq);
+  } else {
+    memset(buf, 0, sizeof(uint16) + sizeof(uint48));
+  }
+
+  buf += sizeof(uint16) + sizeof(uint48);
+
+  memset(buf, 0, sizeof(uint16));
+  return buf + sizeof(uint16);
+}
+
+/**
+ * Initializes \p buf as handshake header. The caller must ensure that \p
+ * buf is capable of holding at least \c sizeof(dtls_handshake_header_t)
+ * bytes. Increments message sequence number counter of \p peer.
+ * \return pointer to the next byte after \p buf
+ */ 
+static inline uint8 *
+dtls_set_handshake_header(uint8 type, dtls_peer_t *peer, 
+			  int length, 
+			  int frag_offset, int frag_length, 
+			  uint8 *buf) {
+  
+  dtls_int_to_uint8(buf, type);
+  buf += sizeof(uint8);
+
+  dtls_int_to_uint24(buf, length);
+  buf += sizeof(uint24);
+
+  if (peer) {
+    /* increment handshake message sequence counter by 1 */
+    inc_uint(uint16, peer->hs_state.mseq);
+  
+    /* and copy the result to buf */
+    memcpy(buf, &peer->hs_state.mseq, sizeof(uint16));
+  } else {
+    memset(buf, 0, sizeof(uint16));    
+  }
+  buf += sizeof(uint16);
+  
+  dtls_int_to_uint24(buf, frag_offset);
+  buf += sizeof(uint24);
+
+  dtls_int_to_uint24(buf, frag_length);
+  buf += sizeof(uint24);
+  
+  return buf;
+}
+
+/** only one compression method is currently defined */
+static uint8 compression_methods[] = {
+  TLS_COMPRESSION_NULL
+};
+
+static inline int is_psk_supported(dtls_context_t *ctx){
+  return ctx && ctx->h && ctx->h->get_psk_key;
+}
+
+static inline int is_ecdsa_supported(dtls_context_t *ctx, int is_client){
+  return ctx && ctx->h && ((!is_client && ctx->h->get_ecdsa_key) || 
+			   (is_client && ctx->h->verify_ecdsa_key));
+}
+
+/**
+ * Returns @c 1 if @p code is a cipher suite other than @c
+ * TLS_NULL_WITH_NULL_NULL that we recognize.
+ *
+ * @param ctx   The current DTLS context
+ * @param code The cipher suite identifier to check
+ * @param is_client 1 for a dtls client, 0 for server
+ * @return @c 1 iff @p code is recognized,
+ */ 
+static int
+known_cipher(dtls_context_t *ctx, dtls_cipher_t code, int is_client) {
+  int psk;
+  int ecdsa;
+
+  psk = is_psk_supported(ctx);
+  ecdsa = is_ecdsa_supported(ctx, is_client);
+  return (psk && code == TLS_PSK_WITH_AES_128_CCM_8) ||
+	 (ecdsa && code == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+}
+
+static void dtls_debug_keyblock(dtls_security_parameters_t *config)
+{
+  DBG("key_block (%d bytes):", dtls_kb_size(config, peer->role));
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  client_MAC_secret",
+			dtls_kb_client_mac_secret(config, peer->role),
+			dtls_kb_mac_secret_size(config, peer->role), 0);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  server_MAC_secret",
+			dtls_kb_server_mac_secret(config, peer->role),
+			dtls_kb_mac_secret_size(config, peer->role), 0);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  client_write_key",
+			dtls_kb_client_write_key(config, peer->role),
+			dtls_kb_key_size(config, peer->role), 0);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  server_write_key",
+			dtls_kb_server_write_key(config, peer->role),
+			dtls_kb_key_size(config, peer->role), 0);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  client_IV",
+			dtls_kb_client_iv(config, peer->role),
+			dtls_kb_iv_size(config, peer->role), 0);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "  server_IV",
+			dtls_kb_server_iv(config, peer->role),
+			dtls_kb_iv_size(config, peer->role), 0);
+}
+
+static int
+calculate_key_block(dtls_context_t *ctx, 
+		    dtls_handshake_parameters_t *handshake,
+		    dtls_security_parameters_t *security,
+		    session_t *session) {
+  unsigned char *pre_master_secret;
+  size_t pre_master_len = 0;
+  pre_master_secret = security->key_block;
+  uint8 master_secret[DTLS_MASTER_SECRET_LENGTH];
+  int err;
+
+  switch (handshake->cipher) {
+  case TLS_PSK_WITH_AES_128_CCM_8: {
+    const dtls_psk_key_t *psk;
+
+    err = CALL(ctx, get_psk_key, session, handshake->keyx.psk.identity,
+	       handshake->keyx.psk.id_length, &psk);
+    if (err < 0) {
+      DBG("no psk key for session available");
+      return err;
+    }
+  /* Temporarily use the key_block storage space for the pre master secret. */
+    pre_master_len = dtls_psk_pre_master_secret(psk->key, psk->key_length, 
+						pre_master_secret);
+
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "psk", psk->key, psk->key_length, 1);
+
+    break;
+  }
+  case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: {
+    pre_master_len = dtls_ecdh_pre_master_secret(handshake->keyx.ecdsa.own_eph_priv,
+						 handshake->keyx.ecdsa.other_eph_pub_x,
+						 handshake->keyx.ecdsa.other_eph_pub_y,
+						 sizeof(handshake->keyx.ecdsa.own_eph_priv),
+						 pre_master_secret);
+    break;
+  }
+  default:
+    DBG("calculate_key_block: unknown cipher");
+    return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+  }
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "client_random", handshake->tmp.random.client,
+			DTLS_RANDOM_LENGTH, 0);
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "server_random", handshake->tmp.random.server,
+			DTLS_RANDOM_LENGTH, 0);
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "pre_master_secret", pre_master_secret,
+			pre_master_len, 0);
+
+  dtls_prf(pre_master_secret, pre_master_len,
+	   PRF_LABEL(master), PRF_LABEL_SIZE(master),
+	   handshake->tmp.random.client, DTLS_RANDOM_LENGTH,
+	   handshake->tmp.random.server, DTLS_RANDOM_LENGTH,
+	   master_secret,
+	   DTLS_MASTER_SECRET_LENGTH);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "master_secret", master_secret,
+			DTLS_MASTER_SECRET_LENGTH, 0);
+
+  /* create key_block from master_secret
+   * key_block = PRF(master_secret,
+                    "key expansion" + tmp.random.server + tmp.random.client) */
+
+  dtls_prf(master_secret,
+	   DTLS_MASTER_SECRET_LENGTH,
+	   PRF_LABEL(key), PRF_LABEL_SIZE(key),
+	   handshake->tmp.random.server, DTLS_RANDOM_LENGTH,
+	   handshake->tmp.random.client, DTLS_RANDOM_LENGTH,
+	   security->key_block,
+	   dtls_kb_size(security, peer->role));
+
+  memcpy(handshake->tmp.master_secret, master_secret, DTLS_MASTER_SECRET_LENGTH);
+  dtls_debug_keyblock(security);
+  return 0;
+}
+
+/**
+ * Releases the storage allocated for read_cipher and write_cipher and
+ * sets both fields to NULL.
+ */
+static void
+invalidate_ciphers(dtls_security_parameters_t *config) {
+  if (config->read_cipher) {
+    dtls_cipher_free(config->read_cipher);
+    config->read_cipher = NULL;
+  }
+  
+  if (config->write_cipher) {
+    dtls_cipher_free(config->write_cipher);
+    config->write_cipher = NULL;
+  }
+}
+
+static int
+init_cipher(dtls_handshake_parameters_t *handshake, dtls_security_parameters_t *config, dtls_peer_type role)
+{
+  /* set crypto context for TLS_PSK_WITH_AES_128_CCM_8 */
+  dtls_cipher_free(config->read_cipher);
+
+  assert(handshake->cipher != TLS_NULL_WITH_NULL_NULL);
+  config->read_cipher = dtls_cipher_new(handshake->cipher,
+					dtls_kb_remote_write_key(config, role),
+					dtls_kb_key_size(config, role));
+
+  if (!config->read_cipher) {
+    WARN("cannot create read cipher");
+    goto error;
+  }
+
+  dtls_cipher_free(config->write_cipher);
+  
+  config->write_cipher = dtls_cipher_new(handshake->cipher,
+					 dtls_kb_local_write_key(config, role),
+					 dtls_kb_key_size(config, role));
+
+  if (!config->write_cipher) {
+    WARN("cannot create write cipher");
+    goto error;
+  }
+
+  config->cipher = handshake->cipher;
+  config->compression = handshake->compression;
+
+  return 0;
+error:
+
+  invalidate_ciphers(config);
+  return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+}
+
+/* TODO: add a generic method which iterates over a list and searches for a specific key */
+static int verify_ext_eliptic_curves(uint8 *data, size_t data_length) {
+  int i, curve_name;
+
+  /* length of curve list */
+  i = dtls_uint16_to_int(data);
+  data += sizeof(uint16);
+  if (i + sizeof(uint16) != data_length) {
+    WARN("the list of the supported elliptic curves should be tls extension length - 2");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  for (i = data_length - sizeof(uint16); i > 0; i -= sizeof(uint16)) {
+    /* check if this curve is supported */
+    curve_name = dtls_uint16_to_int(data);
+    data += sizeof(uint16);
+
+    if (curve_name == TLS_EXT_ELLIPTIC_CURVES_SECP256R1)
+      return 0;
+  }
+
+  WARN("no supported elliptic curve found");
+  return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+}
+
+static int verify_ext_cert_type(uint8 *data, size_t data_length) {
+  int i, cert_type;
+
+  /* length of cert type list */
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  if (i + sizeof(uint8) != data_length) {
+    WARN("the list of the supported certificate types should be tls extension length - 1");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  for (i = data_length - sizeof(uint8); i > 0; i -= sizeof(uint8)) {
+    /* check if this cert type is supported */
+    cert_type = dtls_uint8_to_int(data);
+    data += sizeof(uint8);
+
+    if (cert_type == TLS_CERT_TYPE_OOB)
+      return 0;
+  }
+
+  WARN("no supported certificate type found");
+  return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+}
+
+static int verify_ext_ec_point_formats(uint8 *data, size_t data_length) {
+  int i, cert_type;
+
+  /* length of ec_point_formats list */
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  if (i + sizeof(uint8) != data_length) {
+    WARN("the list of the supported ec_point_formats should be tls extension length - 1");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  for (i = data_length - sizeof(uint8); i > 0; i -= sizeof(uint8)) {
+    /* check if this ec_point_format is supported */
+    cert_type = dtls_uint8_to_int(data);
+    data += sizeof(uint8);
+
+    if (cert_type == TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED)
+      return 0;
+  }
+
+  WARN("no supported ec_point_format found");
+  return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+}
+
+static int
+dtls_check_tls_extension(dtls_peer_t *peer,
+			 uint8 *data, size_t data_length, int client_hello)
+{
+  int i, j;
+  int ext_elliptic_curve = 0;
+  int ext_client_cert_type = 0;
+  int ext_server_cert_type = 0;
+  int ext_ec_point_formats = 0;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+
+  if (data_length < sizeof(uint16)) { 
+    /* no tls extensions specified */
+    if (handshake->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+      goto error;
+    }
+    return 0;
+  }
+
+  /* get the length of the tls extension list */
+  j = dtls_uint16_to_int(data);
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16);
+
+  if (data_length < j)
+    goto error;
+
+  /* check for TLS extensions needed for this cipher */
+  while (data_length) {
+    if (data_length < sizeof(uint16) * 2)
+      goto error;
+
+    /* get the tls extension type */
+    i = dtls_uint16_to_int(data);
+    data += sizeof(uint16);
+    data_length -= sizeof(uint16);
+
+    /* get the length of the tls extension */
+    j = dtls_uint16_to_int(data);
+    data += sizeof(uint16);
+    data_length -= sizeof(uint16);
+
+    if (data_length < j)
+      goto error;
+
+    switch (i) {
+      case TLS_EXT_ELLIPTIC_CURVES:
+        ext_elliptic_curve = 1;
+        if (verify_ext_eliptic_curves(data, j))
+          goto error;
+        break;
+      case TLS_EXT_CLIENT_CERIFICATE_TYPE:
+        ext_client_cert_type = 1;
+        if (client_hello) {
+	  if (verify_ext_cert_type(data, j))
+            goto error;
+        } else {
+	  if (dtls_uint8_to_int(data) != TLS_CERT_TYPE_OOB)
+	    goto error;
+        }
+        break;
+      case TLS_EXT_SERVER_CERIFICATE_TYPE:
+        ext_server_cert_type = 1;
+        if (client_hello) {
+	  if (verify_ext_cert_type(data, j))
+            goto error;
+        } else {
+	  if (dtls_uint8_to_int(data) != TLS_CERT_TYPE_OOB)
+	    goto error;
+        }
+        break;
+      case TLS_EXT_EC_POINT_FORMATS:
+        ext_ec_point_formats = 1;
+        if (verify_ext_ec_point_formats(data, j))
+          goto error;
+        break;
+      default:
+        WARN("unsupported tls extension: %i", i);
+        break;
+    }
+    data += j;
+    data_length -= j;
+  }
+  if (handshake->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+    if (!ext_elliptic_curve && !ext_client_cert_type && !ext_server_cert_type
+	&& !ext_ec_point_formats) {
+      WARN("not all required tls extensions found in client hello");
+      goto error;
+    }
+  }
+  return 0;
+
+error:
+  if (client_hello && peer->state == DTLS_STATE_CONNECTED) {
+    return dtls_alert_create(DTLS_ALERT_LEVEL_WARNING, DTLS_ALERT_NO_RENEGOTIATION);
+  } else {
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+}
+
+/**
+ * Updates the security parameters of given \p peer.  As this must be
+ * done before the new configuration is activated, it changes the
+ * OTHER_CONFIG only. When the ClientHello handshake message in \p
+ * data does not contain a cipher suite or compression method, it is 
+ * copied from the CURRENT_CONFIG.
+ *
+ * \param ctx   The current DTLS context.
+ * \param peer  The remote peer whose security parameters are about to change.
+ * \param data  The handshake message with a ClientHello. 
+ * \param data_length The actual size of \p data.
+ * \return \c 0 if an error occurred, \c 1 otherwise.
+ */
+static int
+dtls_update_parameters(dtls_context_t *ctx, 
+		       dtls_peer_t *peer,
+		       uint8 *data, size_t data_length) {
+  int i, j;
+  int ok;
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+  dtls_security_parameters_t *security = &peer->security_params;
+
+  assert(config);
+  assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH);
+
+  /* DBG("dtls_update_parameters: msglen is %d", data_length); */
+
+  /* skip the handshake header and client version information */
+  data += DTLS_HS_LENGTH + sizeof(uint16);
+  data_length -= DTLS_HS_LENGTH + sizeof(uint16);
+
+  /* store client random in config 
+   * FIXME: if we send the ServerHello here, we do not need to store
+   * the client's random bytes */
+  memcpy(config->tmp.random.client, data, DTLS_RANDOM_LENGTH);
+  data += DTLS_RANDOM_LENGTH;
+  data_length -= DTLS_RANDOM_LENGTH;
+
+  /* Caution: SKIP_VAR_FIELD may jump to error: */
+  SKIP_VAR_FIELD(data, data_length, uint8);	/* skip session id */
+  SKIP_VAR_FIELD(data, data_length, uint8);	/* skip cookie */
+
+  i = dtls_uint16_to_int(data);
+  if (data_length < i + sizeof(uint16)) {
+    /* Looks like we do not have a cipher nor compression. This is ok
+     * for renegotiation, but not for the initial handshake. */
+
+    if (security->cipher == TLS_NULL_WITH_NULL_NULL)
+      goto error;
+
+    config->cipher = security->cipher;
+    config->compression = security->compression;
+
+    return 0;
+  }
+
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16) + i;
+
+  ok = 0;
+  while (i && !ok) {
+    config->cipher = dtls_uint16_to_int(data);
+    ok = known_cipher(ctx, config->cipher, 0);
+    i -= sizeof(uint16);
+    data += sizeof(uint16);
+  }
+
+  /* skip remaining ciphers */
+  data += i;
+
+  if (!ok) {
+    /* reset config cipher to a well-defined value */
+    config->cipher = TLS_NULL_WITH_NULL_NULL;
+    goto error;
+  }
+
+  if (data_length < sizeof(uint8)) { 
+    /* no compression specified, take the current compression method */
+    config->compression = security->compression;
+    goto error;
+  }
+
+  i = dtls_uint8_to_int(data);
+  if (data_length < i + sizeof(uint8))
+    goto error;
+
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8) + i;
+
+  ok = 0;
+  while (i && !ok) {
+    for (j = 0; j < sizeof(compression_methods) / sizeof(uint8); ++j)
+      if (dtls_uint8_to_int(data) == compression_methods[j]) {
+	config->compression = compression_methods[j];
+	ok = 1;
+      }
+    i -= sizeof(uint8);
+    data += sizeof(uint8);    
+  }
+
+  if (!ok) {
+    /* reset config cipher to a well-defined value */
+    goto error;
+  }
+  
+  return dtls_check_tls_extension(peer, data, data_length, 1);
+ error:
+  WARN("ClientHello too short (%d bytes)", data_length);
+  if (peer->state == DTLS_STATE_CONNECTED) {
+    return dtls_alert_create(DTLS_ALERT_LEVEL_WARNING, DTLS_ALERT_NO_RENEGOTIATION);
+  } else {
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+}
+
+static inline int
+check_client_keyexchange(dtls_context_t *ctx, 
+			 dtls_handshake_parameters_t *handshake,
+			 uint8 *data, size_t length) {
+
+  if (handshake->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+
+    if (length < DTLS_HS_LENGTH + DTLS_CKXEC_LENGTH) {
+      DBG("The client key exchange is too short");
+      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+    }
+    data += DTLS_HS_LENGTH;
+
+    if (dtls_uint8_to_int(data) != 65) {
+      DBG("expected 65 bytes long public point");
+      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+    }
+    data += sizeof(uint8);
+
+    if (dtls_uint8_to_int(data) != 4) {
+      DBG("expected uncompressed public point");
+      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+    }
+    data += sizeof(uint8);
+
+    memcpy(handshake->keyx.ecdsa.other_eph_pub_x, data,
+	   sizeof(handshake->keyx.ecdsa.other_eph_pub_x));
+    data += sizeof(handshake->keyx.ecdsa.other_eph_pub_x);
+
+    memcpy(handshake->keyx.ecdsa.other_eph_pub_y, data,
+	   sizeof(handshake->keyx.ecdsa.other_eph_pub_y));
+    data += sizeof(handshake->keyx.ecdsa.other_eph_pub_y);
+  } else {
+    int id_length;
+
+    if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) {
+      DBG("The client key exchange is too short");
+      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+    }
+    data += DTLS_HS_LENGTH;
+
+    id_length = dtls_uint16_to_int(data);
+    data += sizeof(uint16);
+
+    if (DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN + id_length != length) {
+      DBG("The identity has a wrong length");
+      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+    }
+
+    if (id_length > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
+      WARN("please use a smaller client identity");
+      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+    }
+
+    handshake->keyx.psk.id_length = id_length;
+    memcpy(handshake->keyx.psk.identity, data, id_length);
+  }
+  return 0;
+}
+
+static inline void
+update_hs_hash(dtls_peer_t *peer, uint8 *data, size_t length) {
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "add MAC data", data, length, 0);
+  dtls_hash_update(&peer->hs_state.hs_hash, data, length);
+}
+
+static void
+copy_hs_hash(dtls_peer_t *peer, dtls_hash_ctx *hs_hash) {
+  memcpy(hs_hash, &peer->hs_state.hs_hash, sizeof(peer->hs_state.hs_hash));
+}
+
+static inline size_t
+finalize_hs_hash(dtls_peer_t *peer, uint8 *buf) {
+  return dtls_hash_finalize(buf, &peer->hs_state.hs_hash);
+}
+
+static inline void
+clear_hs_hash(dtls_peer_t *peer) {
+  assert(peer);
+  DBG("clear MAC");
+  dtls_hash_init(&peer->hs_state.hs_hash);
+}
+
+/** 
+ *Checks if \p record + \p data contain a Finished message with valid
+ * verify_data. 
+ *
+ * \param ctx    The current DTLS context.
+ * \param peer   The remote peer of the security association.
+ * \param record The message record header.
+ * \param rlen   The actual length of \p record.
+ * \param data   The cleartext payload of the message.
+ * \param data_length Actual length of \p data.
+ * \return \c 1 if the Finished message is valid, \c 0 otherwise.
+ */
+static int
+check_finished(dtls_context_t *ctx, dtls_peer_t *peer,
+	       uint8 *record, uint8 *data, size_t data_length) {
+	       
+	       DBG("check_finished");
+  size_t digest_length, label_size;
+  const unsigned char *label;
+  unsigned char buf[DTLS_HMAC_MAX];
+
+  if (data_length < DTLS_HS_LENGTH + DTLS_FIN_LENGTH)
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+
+  /* Use a union here to ensure that sufficient stack space is
+   * reserved. As statebuf and verify_data are not used at the same
+   * time, we can re-use the storage safely.
+   */
+  union {
+    unsigned char statebuf[DTLS_HASH_CTX_SIZE];
+    unsigned char verify_data[DTLS_FIN_LENGTH];
+  } b;
+
+  /* temporarily store hash status for roll-back after finalize */
+  memcpy(b.statebuf, &peer->hs_state.hs_hash, DTLS_HASH_CTX_SIZE);
+
+  digest_length = finalize_hs_hash(peer, buf);
+  /* clear_hash(); */
+
+  /* restore hash status */
+  memcpy(&peer->hs_state.hs_hash, b.statebuf, DTLS_HASH_CTX_SIZE);
+
+  if (peer->role == DTLS_CLIENT) {
+    label = PRF_LABEL(server);
+    label_size = PRF_LABEL_SIZE(server);
+  } else { /* client */
+    label = PRF_LABEL(client);
+    label_size = PRF_LABEL_SIZE(client);
+  }
+
+  dtls_prf(peer->handshake_params.tmp.master_secret,
+	   DTLS_MASTER_SECRET_LENGTH,
+	   label, label_size,
+	   PRF_LABEL(finished), PRF_LABEL_SIZE(finished),
+	   buf, digest_length,
+	   b.verify_data, sizeof(b.verify_data));
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "d:", data + DTLS_HS_LENGTH, sizeof(b.verify_data), 0);
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "v:", b.verify_data, sizeof(b.verify_data), 0);
+  return memcmp(data + DTLS_HS_LENGTH, b.verify_data, sizeof(b.verify_data));
+}
+
+/**
+ * Prepares the payload given in \p data for sending with
+ * dtls_send(). The \p data is encrypted and compressed according to
+ * the current security parameters of \p peer.  The result of this
+ * operation is put into \p sendbuf with a prepended record header of
+ * type \p type ready for sending. As some cipher suites add a MAC
+ * before encryption, \p data must be large enough to hold this data
+ * as well (usually \c dtls_kb_digest_size(CURRENT_CONFIG(peer)).
+ *
+ * \param peer    The remote peer the packet will be sent to.
+ * \param type    The content type of this record.
+ * \param data    The payload to send.
+ * \param data_length The size of \p data.
+ * \param sendbuf The output buffer where the encrypted record
+ *                will be placed.
+ * \param rlen    This parameter must be initialized with the 
+ *                maximum size of \p sendbuf and will be updated
+ *                to hold the actual size of the stored packet
+ *                on success. On error, the value of \p rlen is
+ *                undefined. 
+ * \return Less than zero on error, or greater than zero success.
+ */
+static int
+dtls_prepare_record(dtls_peer_t *peer,
+		    unsigned char type,
+		    uint8 *data_array[], size_t data_len_array[],
+		    size_t data_array_len,
+		    uint8 *sendbuf, size_t *rlen) {
+  DBG("dtls_prepare_record");    
+		
+  uint8 *p, *start;
+  int res;
+  int i;
+  dtls_security_parameters_t *security = &peer->security_params;
+  
+  p = dtls_set_record_header(type, peer, sendbuf);
+  start = p;
+
+  if (!peer || security->cipher == TLS_NULL_WITH_NULL_NULL) {
+    /* no cipher suite */
+
+    res = 0;
+    for (i = 0; i < data_array_len; i++) {
+      /* check the minimum that we need for packets that are not encrypted */
+      if (*rlen < (p - start) + data_len_array[i]) {
+        DBG("dtls_prepare_record: send buffer too small");
+        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+      }
+
+      memcpy(p, data_array[i], data_len_array[i]);
+      p += data_len_array[i];
+      res += data_len_array[i];
+    }
+  } else { /* TLS_PSK_WITH_AES_128_CCM_8 */   
+    dtls_cipher_context_t *cipher_context;
+
+    /** 
+     * length of additional_data for the AEAD cipher which consists of
+     * seq_num(2+6) + type(1) + version(2) + length(2)
+     */
+#define A_DATA_LEN 13
+    unsigned char nonce[DTLS_CCM_BLOCKSIZE];
+    unsigned char A_DATA[A_DATA_LEN];
+
+    if (security->cipher == TLS_PSK_WITH_AES_128_CCM_8) {
+      DBG("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_8");
+    } else if (security->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+      DBG("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8");
+    } else {
+      DBG("dtls_prepare_record(): encrypt using unknown cipher");
+    }
+
+    /* set nonce       
+       from http://tools.ietf.org/html/draft-mcgrew-tls-aes-ccm-03:
+        struct {
+               case client:
+                  uint32 client_write_IV;  // low order 32-bits
+               case server:
+                  uint32 server_write_IV;  // low order 32-bits
+               uint64 seq_num;
+            } CCMNonce.
+
+	    In DTLS, the 64-bit seq_num is the 16-bit epoch concatenated with the
+	    48-bit seq_num.
+    */
+
+    memcpy(p, &DTLS_RECORD_HEADER(sendbuf)->epoch, 8);
+    p += 8;
+    res = 8;
+
+    for (i = 0; i < data_array_len; i++) {
+      /* check the minimum that we need for packets that are not encrypted */
+      if (*rlen < res + data_len_array[i]) {
+        DBG("dtls_prepare_record: send buffer too small");
+        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+      }
+
+      memcpy(p, data_array[i], data_len_array[i]);
+      p += data_len_array[i];
+      res += data_len_array[i];
+    }
+
+    memset(nonce, 0, DTLS_CCM_BLOCKSIZE);
+    memcpy(nonce, dtls_kb_local_iv(security, peer->role),
+	   dtls_kb_iv_size(security, peer->role));
+    memcpy(nonce + dtls_kb_iv_size(security, peer->role), start, 8); /* epoch + seq_num */
+
+    cipher_context = security->write_cipher;
+
+    if (!cipher_context) {
+      WARN("no write_cipher available!");
+      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+    }
+
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "nonce:", nonce, DTLS_CCM_BLOCKSIZE, 0);
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "key:",
+			  dtls_kb_local_write_key(security, peer->role),
+			  dtls_kb_key_size(security, peer->role), 0);
+    
+    /* re-use N to create additional data according to RFC 5246, Section 6.2.3.3:
+     * 
+     * additional_data = seq_num + TLSCompressed.type +
+     *                   TLSCompressed.version + TLSCompressed.length;
+     */
+    memcpy(A_DATA, &DTLS_RECORD_HEADER(sendbuf)->epoch, 8); /* epoch and seq_num */
+    memcpy(A_DATA + 8,  &DTLS_RECORD_HEADER(sendbuf)->content_type, 3); /* type and version */
+    dtls_int_to_uint16(A_DATA + 11, res - 8); /* length */
+    
+    res = dtls_encrypt(cipher_context, start + 8, res - 8, start + 8, nonce,
+		       A_DATA, A_DATA_LEN);
+
+    if (res < 0)
+      return res;
+
+    res += 8;			/* increment res by size of nonce_explicit */
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "message:", start, res, 0);
+  }
+
+  /* fix length of fragment in sendbuf */
+  dtls_int_to_uint16(sendbuf + 11, res);
+  
+  *rlen = DTLS_RH_LENGTH + res;
+  return 0;
+}
+
+static int
+dtls_send_handshake_msg_hash(dtls_context_t *ctx,
+			     dtls_peer_t *peer,
+			     session_t *session,
+			     uint8 header_type,
+			     uint8 *data, size_t data_length,
+			     int add_hash)
+{
+  DBG("dtls_send_handshake_msg_hash");
+  uint8 buf[DTLS_HS_LENGTH];
+  uint8 *data_array[2];
+  size_t data_len_array[2];
+  int i = 0;
+  uint8 sendbuf[DTLS_MAX_BUF];
+  size_t len = sizeof(sendbuf);
+
+  dtls_set_handshake_header(header_type, (add_hash) ? peer : NULL, data_length, 0,
+			    data_length, buf);
+
+  if (add_hash) {
+    update_hs_hash(peer, buf, sizeof(buf));
+  }
+  data_array[i] = buf;
+  data_len_array[i] = sizeof(buf);
+  i++;
+
+  if (data != NULL) {
+    if (add_hash) {
+      update_hs_hash(peer, data, data_length);
+    }
+    data_array[i] = data;
+    data_len_array[i] = data_length;
+    i++;
+  }
+  i = dtls_prepare_record(peer, DTLS_CT_HANDSHAKE, data_array, data_len_array,
+			  i, sendbuf, &len);
+  if (i < 0) {
+    return i;
+  }
+
+  return CALL(ctx, write, session, sendbuf, len);
+}
+
+static int
+dtls_send_handshake_msg(dtls_context_t *ctx,
+			dtls_peer_t *peer,
+			uint8 header_type,
+			uint8 *data, size_t data_length)
+{
+  return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
+				      header_type, data, data_length, 1);
+}
+
+/** 
+ * Returns true if the message @p Data is a handshake message that
+ * must be included in the calculation of verify_data in the Finished
+ * message.
+ * 
+ * @param Type The message type. Only handshake messages but the initial 
+ * Client Hello and Hello Verify Request are included in the hash,
+ * @param Data The PDU to examine.
+ * @param Length The length of @p Data.
+ * 
+ * @return @c 1 if @p Data must be included in hash, @c 0 otherwise.
+ *
+ * @hideinitializer
+ */
+#define MUST_HASH(Type, Data, Length)					\
+  ((Type) == DTLS_CT_HANDSHAKE &&					\
+   ((Data) != NULL) && ((Length) > 0)  &&				\
+   ((Data)[0] != DTLS_HT_HELLO_VERIFY_REQUEST) &&			\
+   ((Data)[0] != DTLS_HT_CLIENT_HELLO ||				\
+    ((Length) >= HS_HDR_LENGTH &&					\
+     (dtls_uint16_to_int(DTLS_RECORD_HEADER(Data)->epoch > 0) ||	\
+      (dtls_uint16_to_int(HANDSHAKE(Data)->message_seq) > 0)))))
+
+/**
+ * Sends the data passed in @p buf as a DTLS record of type @p type to
+ * the given peer. The data will be encrypted and compressed according
+ * to the security parameters for @p peer.
+ *
+ * @param ctx    The DTLS context in effect.
+ * @param peer   The remote party where the packet is sent.
+ * @param type   The content type of this record.
+ * @param buf    The data to send.
+ * @param buflen The number of bytes to send from @p buf.
+ * @return Less than zero in case of an error or the number of
+ *   bytes that have been sent otherwise.
+ */
+static int
+dtls_send(dtls_context_t *ctx, dtls_peer_t *peer,
+	  unsigned char type,
+	  uint8 *buf, size_t buflen) {
+  
+  /* We cannot use ctx->sendbuf here as it is reserved for collecting
+   * the input for this function, i.e. buf == ctx->sendbuf.
+   *
+   * TODO: check if we can use the receive buf here. This would mean
+   * that we might not be able to handle multiple records stuffed in
+   * one UDP datagram */
+  unsigned char sendbuf[DTLS_MAX_BUF];
+  size_t len = sizeof(sendbuf);
+  int res;
+
+  res = dtls_prepare_record(peer, type, &buf, &buflen, 1, sendbuf, &len);
+
+  if (res < 0)
+    return res;
+
+  /* if (peer && MUST_HASH(peer, type, buf, buflen)) */
+  /*   update_hs_hash(peer, buf, buflen); */
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "send header", sendbuf,
+			sizeof(dtls_record_header_t), 1);
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "send unencrypted", buf, buflen, 1);
+
+  if (type == DTLS_CT_HANDSHAKE && buf[0] != DTLS_HT_HELLO_VERIFY_REQUEST) {
+    /* copy handshake messages other than HelloVerify into retransmit buffer */
+    netq_t *n = netq_node_new();
+    if (n) {
+      dtls_tick_t now;
+      dtls_ticks(&now);
+      n->t = now + 2 * CLOCK_SECOND;
+      n->retransmit_cnt = 0;
+      n->timeout = 2 * CLOCK_SECOND;
+      n->peer = peer;
+      n->length = buflen;
+      memcpy(n->data, buf, buflen);
+
+      if (!netq_insert_node((netq_t **)ctx->sendqueue, n)) {
+	WARN("cannot add packet to retransmit buffer");
+	netq_node_free(n);
+#ifdef WITH_CONTIKI
+      } else {
+	/* must set timer within the context of the retransmit process */
+	PROCESS_CONTEXT_BEGIN(&dtls_retransmit_process);
+	etimer_set(&ctx->retransmit_timer, n->timeout);
+	PROCESS_CONTEXT_END(&dtls_retransmit_process);
+#else /* WITH_CONTIKI */
+	DBG("copied to sendqueue");
+#endif /* WITH_CONTIKI */
+      }
+    } else 
+      WARN("retransmit buffer full");
+  }
+
+  /* FIXME: copy to peer's sendqueue (after fragmentation if
+   * necessary) and initialize retransmit timer */
+  res = CALL(ctx, write, &peer->session, sendbuf, len);
+
+  /* Guess number of bytes application data actually sent:
+   * dtls_prepare_record() tells us in len the number of bytes to
+   * send, res will contain the bytes actually sent. */
+  return res <= 0 ? res : buflen - (len - res);
+}
+
+static inline int
+dtls_alert(dtls_context_t *ctx, dtls_peer_t *peer, dtls_alert_level_t level,
+	   dtls_alert_t description) {
+  uint8_t msg[] = { level, description };
+
+  dtls_send(ctx, peer, DTLS_CT_ALERT, msg, sizeof(msg));
+  return 0;
+}
+
+int 
+dtls_close(dtls_context_t *ctx, const session_t *remote) {
+  int res = -1;
+  dtls_peer_t *peer;
+
+  peer = dtls_get_peer(ctx, remote);
+
+  if (peer) {
+    res = dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE_NOTIFY);
+    /* indicate tear down */
+    peer->state = DTLS_STATE_CLOSING;
+  }
+  return res;
+}
+
+static void dtls_destory_peer(dtls_context_t *ctx, dtls_peer_t *peer, int unlink)
+{
+  if (peer->state != DTLS_STATE_CLOSED)
+    dtls_close(ctx, &peer->session);
+  if (unlink) {
+#ifndef WITH_CONTIKI
+    HASH_DEL_PEER(ctx->peers, peer);
+#else /* WITH_CONTIKI */
+    list_remove(ctx->peers, peer);
+
+#ifndef NDEBUG
+    PRINTF("removed peer [");
+    PRINT6ADDR(&peer->session.addr);
+    PRINTF("]:%d", uip_ntohs(peer->session.port));
+#endif
+#endif /* WITH_CONTIKI */
+  }
+  dtls_free_peer(peer);
+}
+
+/**
+ * Checks a received Client Hello message for a valid cookie. When the
+ * Client Hello contains no cookie, the function fails and a Hello
+ * Verify Request is sent to the peer (using the write callback function
+ * registered with \p ctx). The return value is \c -1 on error, \c 0 when
+ * undecided, and \c 1 if the Client Hello was good. 
+ * 
+ * \param ctx     The DTLS context.
+ * \param peer    The remote party we are talking to, if any.
+ * \param session Transport address of the remote peer.
+ * \param msg     The received datagram.
+ * \param msglen  Length of \p msg.
+ * \return \c 1 if msg is a Client Hello with a valid cookie, \c 0 or
+ * \c -1 otherwise.
+ */
+static int
+dtls_verify_peer(dtls_context_t *ctx, 
+		 dtls_peer_t *peer, 
+		 session_t *session,
+		 uint8 *record, 
+		 uint8 *data, size_t data_length)
+{
+
+DBG("dtls_verify_peer");
+
+  uint8 buf[DTLS_HV_LENGTH + DTLS_COOKIE_LENGTH];
+  uint8 *p = buf;
+  int len = DTLS_COOKIE_LENGTH;
+  uint8 *cookie;
+  int err;
+#undef mycookie
+#define mycookie (buf + DTLS_HV_LENGTH)
+
+  /* Store cookie where we can reuse it for the HelloVerify request. */
+  err = dtls_create_cookie(ctx, session, data, data_length, mycookie, &len);
+  if (err < 0)
+    return err;
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "create cookie", mycookie, len, 0);
+
+  assert(len == DTLS_COOKIE_LENGTH);
+    
+  /* Perform cookie check. */
+  len = dtls_get_cookie(data, data_length, &cookie);
+  if (len < 0) {
+    WARN("error while fetching the cookie, err: %i", err);
+    return err;
+  }
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "compare with cookie", cookie, len, 0);
+
+  /* check if cookies match */
+  if (len == DTLS_COOKIE_LENGTH && memcmp(cookie, mycookie, len) == 0) {
+    DBG("found matching cookie");
+    return 0;
+  }
+
+  if (len > 0) {
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "invalid cookie", cookie, len, 0);
+  } else {
+    DBG("cookie len is 0!");
+  }
+
+  /* ClientHello did not contain any valid cookie, hence we send a
+   * HelloVerify request. */
+
+  dtls_int_to_uint16(p, DTLS_VERSION);
+  p += sizeof(uint16);
+
+  dtls_int_to_uint8(p, DTLS_COOKIE_LENGTH);
+  p += sizeof(uint8);
+
+  assert(p == mycookie);
+
+  p += DTLS_COOKIE_LENGTH;
+
+  err = dtls_send_handshake_msg_hash(ctx, peer, session,
+				     DTLS_HT_HELLO_VERIFY_REQUEST,
+				     buf, p - buf, 0);
+  if (err < 0) {
+    WARN("cannot send HelloVerify request");
+  }
+  return err; /* HelloVerify is sent, now we cannot do anything but wait */
+
+#undef mycookie
+}
+
+static int
+check_client_certificate_verify(dtls_context_t *ctx, 
+				dtls_peer_t *peer,
+				uint8 *data, size_t data_length)
+{
+   DBG("check_client_certificate_verify");
+
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+  int i;
+  unsigned char *result_r;
+  unsigned char *result_s;
+  dtls_hash_ctx hs_hash;
+  unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE];
+
+  assert(config->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+
+  data += DTLS_HS_LENGTH;
+
+  if (data_length < DTLS_HS_LENGTH + DTLS_CV_LENGTH) {
+    DBG("the package length does not match the expected");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  if (dtls_uint8_to_int(data) != TLS_EXT_SIG_HASH_ALGO_SHA256) {
+    DBG("only sha256 is supported in certificate verify");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (dtls_uint8_to_int(data) != TLS_EXT_SIG_HASH_ALGO_ECDSA) {
+    DBG("only ecdsa signature is supported in client verify");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (data_length < dtls_uint16_to_int(data)) {
+    DBG("signature length wrong");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16);
+
+  if (dtls_uint8_to_int(data) != 0x30) {
+    DBG("wrong ASN.1 struct, expected SEQUENCE");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (data_length < dtls_uint8_to_int(data)) {
+    DBG("signature length wrong");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (dtls_uint8_to_int(data) != 0x02) {
+    DBG("wrong ASN.1 struct, expected Integer");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  /* Sometimes these values have a leeding 0 byte */
+  result_r = data + i - DTLS_EC_KEY_SIZE;
+
+  data += i;
+  data_length -= i;
+
+  if (dtls_uint8_to_int(data) != 0x02) {
+    DBG("wrong ASN.1 struct, expected Integer");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  /* Sometimes these values have a leeding 0 byte */
+  result_s = data + i - DTLS_EC_KEY_SIZE;
+
+  data += i;
+  data_length -= i;
+
+  copy_hs_hash(peer, &hs_hash);
+
+  dtls_hash_finalize(sha256hash, &hs_hash);
+
+  i = dtls_ecdsa_verify_sig_hash(config->keyx.ecdsa.other_pub_x, config->keyx.ecdsa.other_pub_y,
+			    sizeof(config->keyx.ecdsa.other_pub_x),
+			    sha256hash, sizeof(sha256hash),
+			    result_r, result_s);
+
+  if (i < 0) {
+    DBG("wrong signature err: %i", i);
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  return 0;
+}
+
+static int
+dtls_send_server_hello(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+  /* Ensure that the largest message to create fits in our source
+   * buffer. (The size of the destination buffer is checked by the
+   * encoding function, so we do not need to guess.) */
+  uint8 buf[DTLS_SH_LENGTH + 2 + 5 + 5 + 8 + 6];
+  uint8 *p;
+  int ecdsa;
+  uint8 extension_size;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+  dtls_tick_t now;
+
+  ecdsa = handshake->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
+
+  extension_size = (ecdsa) ? 2 + 5 + 5 + 8 + 6 : 0;
+
+  /* Handshake header */
+  p = buf;
+
+  /* ServerHello */
+  dtls_int_to_uint16(p, DTLS_VERSION);
+  p += sizeof(uint16);
+
+  /* Set server random: First 4 bytes are the server's Unix timestamp,
+   * followed by 28 bytes of generate random data. */
+  dtls_ticks(&now);
+  dtls_int_to_uint32(handshake->tmp.random.server, now / CLOCK_SECOND);
+  prng(handshake->tmp.random.server + 4, 28);
+
+  memcpy(p, handshake->tmp.random.server, DTLS_RANDOM_LENGTH);
+  p += DTLS_RANDOM_LENGTH;
+
+  *p++ = 0;			/* no session id */
+
+  if (handshake->cipher != TLS_NULL_WITH_NULL_NULL) {
+    /* selected cipher suite */
+    dtls_int_to_uint16(p, handshake->cipher);
+    p += sizeof(uint16);
+
+    /* selected compression method */
+    if (handshake->compression >= 0)
+      *p++ = compression_methods[handshake->compression];
+  }
+
+  if (extension_size) {
+    /* length of the extensions */
+    dtls_int_to_uint16(p, extension_size - 2);
+    p += sizeof(uint16);
+  }
+
+  if (ecdsa) {
+    /* client certificate type extension */
+    dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERIFICATE_TYPE);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 1);
+    p += sizeof(uint16);
+
+    dtls_int_to_uint8(p, TLS_CERT_TYPE_OOB);
+    p += sizeof(uint8);
+
+    /* client certificate type extension */
+    dtls_int_to_uint16(p, TLS_EXT_SERVER_CERIFICATE_TYPE);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 1);
+    p += sizeof(uint16);
+
+    dtls_int_to_uint8(p, TLS_CERT_TYPE_OOB);
+    p += sizeof(uint8);
+
+    /* elliptic_curves */
+    dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 4);
+    p += sizeof(uint16);
+
+    /* length of the list */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES_SECP256R1);
+    p += sizeof(uint16);
+
+    /* ec_point_formats */
+    dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    /* number of supported formats */
+    dtls_int_to_uint8(p, 1);
+    p += sizeof(uint8);
+
+    dtls_int_to_uint8(p, TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED);
+    p += sizeof(uint8);
+  }
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_HELLO,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_certificate_ecdsa(dtls_context_t *ctx, dtls_peer_t *peer,
+			    const dtls_ecdsa_key_t *key)
+{
+  DBG("dtls_send_certificate_ecdsa");
+  uint8 buf[DTLS_CE_LENGTH];
+  uint8 *p;
+
+  /* Certificate 
+   *
+   * Start message construction at beginning of buffer. */
+  p = buf;
+
+  dtls_int_to_uint24(p, 94);
+  p += sizeof(uint24);
+
+  dtls_int_to_uint24(p, 91);
+  p += sizeof(uint24);
+  
+  memcpy(p, &cert_asn1_header, sizeof(cert_asn1_header));
+  p += sizeof(cert_asn1_header);
+
+  memcpy(p, key->pub_key_x, DTLS_EC_KEY_SIZE);
+  p += DTLS_EC_KEY_SIZE;
+
+  memcpy(p, key->pub_key_y, DTLS_EC_KEY_SIZE);
+  p += DTLS_EC_KEY_SIZE;
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE,
+				 buf, p - buf);
+}
+
+static uint8 *
+dtls_add_ecdsa_signature_elem(uint8 *p, uint32_t *point_r, uint32_t *point_s)
+{
+  int len_r;
+  int len_s;
+
+#define R_KEY_OFFSET (2 + 1 + 1 + 1 + 1)
+#define S_KEY_OFFSET(len_s) (R_KEY_OFFSET + (len_s) + 1 + 1)
+  /* store the pointer to the r component of the signature and make space */
+  len_r = dtls_ec_key_from_uint32_asn1(point_r, DTLS_EC_KEY_SIZE, p + R_KEY_OFFSET);
+  len_s = dtls_ec_key_from_uint32_asn1(point_s, DTLS_EC_KEY_SIZE, p + S_KEY_OFFSET(len_r));
+
+#undef R_KEY_OFFSET
+#undef S_KEY_OFFSET
+
+  /* length of signature */
+  dtls_int_to_uint16(p, len_r + len_s + 2 + 2 + 2);
+  p += sizeof(uint16);
+
+  /* ASN.1 SEQUENCE */
+  dtls_int_to_uint8(p, 0x30);
+  p += sizeof(uint8);
+
+  dtls_int_to_uint8(p, len_r + len_s + 2 + 2);
+  p += sizeof(uint8);
+
+  /* ASN.1 Integer r */
+  dtls_int_to_uint8(p, 0x02);
+  p += sizeof(uint8);
+
+  dtls_int_to_uint8(p, len_r);
+  p += sizeof(uint8);
+
+  /* the pint r was added here */
+  p += len_r;
+
+  /* ASN.1 Integer s */
+  dtls_int_to_uint8(p, 0x02);
+  p += sizeof(uint8);
+
+  dtls_int_to_uint8(p, len_s);
+  p += sizeof(uint8);
+
+  /* the pint s was added here */
+  p += len_s;
+
+  return p;
+}
+
+static int
+dtls_send_server_key_exchange_ecdh(dtls_context_t *ctx, dtls_peer_t *peer,
+				   const dtls_ecdsa_key_t *key)
+{
+  /* The ASN.1 Integer representation of an 32 byte unsigned int could be
+   * 33 bytes long add space for that */
+  uint8 buf[DTLS_SKEXEC_LENGTH + 2];
+  uint8 *p;
+  uint8 *key_params;
+  uint8 *ephemeral_pub_x;
+  uint8 *ephemeral_pub_y;
+  uint32_t point_r[9];
+  uint32_t point_s[9];
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+
+  /* ServerKeyExchange 
+   *
+   * Start message construction at beginning of buffer. */
+  p = buf;
+
+  key_params = p;
+  /* ECCurveType curve_type: named_curve */
+  dtls_int_to_uint8(p, 3);
+  p += sizeof(uint8);
+
+  /* NamedCurve namedcurve: secp256r1 */
+  dtls_int_to_uint16(p, 23);
+  p += sizeof(uint16);
+
+  dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE);
+  p += sizeof(uint8);
+
+  /* This should be an uncompressed point, but I do not have access to the sepc. */
+  dtls_int_to_uint8(p, 4);
+  p += sizeof(uint8);
+
+  /* store the pointer to the x component of the pub key and make space */
+  ephemeral_pub_x = p;
+  p += DTLS_EC_KEY_SIZE;
+
+  /* store the pointer to the y component of the pub key and make space */
+  ephemeral_pub_y = p;
+  p += DTLS_EC_KEY_SIZE;
+
+  dtls_ecdsa_generate_key(config->keyx.ecdsa.own_eph_priv,
+			  ephemeral_pub_x, ephemeral_pub_y,
+			  DTLS_EC_KEY_SIZE);
+
+  /* sign the ephemeral and its paramaters */
+  dtls_ecdsa_create_sig(key->priv_key, DTLS_EC_KEY_SIZE,
+		       config->tmp.random.client, DTLS_RANDOM_LENGTH,
+		       config->tmp.random.server, DTLS_RANDOM_LENGTH,
+		       key_params, p - key_params,
+		       point_r, point_s);
+
+  p = dtls_add_ecdsa_signature_elem(p, point_r, point_s);
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_server_key_exchange_psk(dtls_context_t *ctx, dtls_peer_t *peer,
+				   const dtls_psk_key_t *key)
+{
+  uint8 buf[DTLS_SKEXECPSK_LENGTH_MAX];
+  uint8 *p;
+
+  p = buf;
+
+  if (key->id_length > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
+    WARN("psk identity hint is too long");
+    return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+  }
+
+  dtls_int_to_uint16(p, key->id_length);
+  p += sizeof(uint16);
+
+  memcpy(p, key->id, key->id_length);
+  p += key->id_length;
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_server_certificate_request(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+  uint8 buf[8];
+  uint8 *p;
+
+  /* ServerHelloDone 
+   *
+   * Start message construction at beginning of buffer. */
+  p = buf;
+
+  /* certificate_types */
+  dtls_int_to_uint8(p, 1);
+  p += sizeof(uint8);
+
+  /* ecdsa_sign */
+  dtls_int_to_uint8(p, 64);
+  p += sizeof(uint8);
+
+  /* supported_signature_algorithms */
+  dtls_int_to_uint16(p, 2);
+  p += sizeof(uint16);
+
+  /* sha256 */
+  dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_SHA256);
+  p += sizeof(uint8);
+
+  /* ecdsa */
+  dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_ECDSA);
+  p += sizeof(uint8);
+
+  /* certificate_authoritiess */
+  dtls_int_to_uint16(p, 0);
+  p += sizeof(uint16);
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_REQUEST,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_server_hello_done(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+
+  /* ServerHelloDone 
+   *
+   * Start message construction at beginning of buffer. */
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_HELLO_DONE,
+				 NULL, 0);
+}
+
+static int
+dtls_send_server_hello_msgs(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+  int res;
+
+  res = dtls_send_server_hello(ctx, peer);
+
+  if (res < 0) {
+    DBG("dtls_server_hello: cannot prepare ServerHello record");
+    return res;
+  }
+
+  if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+    const dtls_ecdsa_key_t *ecdsa_key;
+
+    res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
+    if (res < 0) {
+      DBG("no ecdsa certificate to send in certificate");
+      return res;
+    }
+
+    res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
+
+    if (res < 0) {
+      DBG("dtls_server_hello: cannot prepare Certificate record");
+      return res;
+    }
+
+    res = dtls_send_server_key_exchange_ecdh(ctx, peer, ecdsa_key);
+
+    if (res < 0) {
+      DBG("dtls_server_hello: cannot prepare Server Key Exchange record");
+      return res;
+    }
+
+    if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 &&
+	ctx && ctx->h && ctx->h->verify_ecdsa_key) {
+      res = dtls_send_server_certificate_request(ctx, peer);
+
+      if (res < 0) {
+        DBG("dtls_server_hello: cannot prepare certificate Request record");
+        return res;
+      }
+    }
+  } else if (peer->handshake_params.cipher == TLS_PSK_WITH_AES_128_CCM_8) {
+    const dtls_psk_key_t *psk;
+
+    res = CALL(ctx, get_psk_key, &peer->session, NULL, 0, &psk);
+    if (res < 0) {
+      DBG("no psk or identity found for this session");
+      return res;
+    }
+
+    if (psk->id_length) {
+      res = dtls_send_server_key_exchange_psk(ctx, peer, psk);
+
+      if (res < 0) {
+	DBG("dtls_server_key_exchange_psk: cannot send server key exchange record");
+	return res;
+      }
+    }
+  }
+
+  res = dtls_send_server_hello_done(ctx, peer);
+
+  if (res < 0) {
+    DBG("dtls_server_hello: cannot prepare ServerHelloDone record");
+    return res;
+  }
+  return 0;
+}
+
+static inline int 
+dtls_send_ccs(dtls_context_t *ctx, dtls_peer_t *peer) {
+  uint8 buf[1];
+  buf[0] = 1;
+
+  return dtls_send(ctx, peer, DTLS_CT_CHANGE_CIPHER_SPEC, buf, 1);
+}
+
+    
+static int
+dtls_send_client_key_exchange(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+  uint8 buf[DTLS_CKXEC_LENGTH];
+  uint8 *p;
+  int err;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+
+  p = buf;
+
+  switch (handshake->cipher) {
+  case TLS_PSK_WITH_AES_128_CCM_8: {
+    const dtls_psk_key_t *psk;
+
+    err = CALL(ctx, get_psk_key, &peer->session, handshake->keyx.psk.identity,
+	       handshake->keyx.psk.id_length, &psk);
+    if (err < 0) {
+      DBG("no psk key to send in kx");
+      return err;
+    }
+
+    if (psk->id_length + sizeof(uint16) > DTLS_CKXEC_LENGTH) {
+      WARN("the psk identity is too long");
+      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+    }
+
+    dtls_int_to_uint16(p, psk->id_length);
+    p += sizeof(uint16);
+
+    memcpy(p, psk->id, psk->id_length);
+    p += psk->id_length;
+
+    break;
+  }
+  case TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8: {
+    uint8 *ephemeral_pub_x;
+    uint8 *ephemeral_pub_y;
+
+    dtls_int_to_uint8(p, 1 + 2 * DTLS_EC_KEY_SIZE);
+    p += sizeof(uint8);
+
+    /* This should be an uncompressed point, but I do not have access to the sepc. */
+    dtls_int_to_uint8(p, 4);
+    p += sizeof(uint8);
+
+    ephemeral_pub_x = p;
+    p += DTLS_EC_KEY_SIZE;
+    ephemeral_pub_y = p;
+    p += DTLS_EC_KEY_SIZE;
+
+    dtls_ecdsa_generate_key(peer->handshake_params.keyx.ecdsa.own_eph_priv,
+    			    ephemeral_pub_x, ephemeral_pub_y,
+    			    DTLS_EC_KEY_SIZE);
+
+    break;
+  }
+  default:
+    DBG("cipher not supported");
+    return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+  }
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CLIENT_KEY_EXCHANGE,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_certificate_verify_ecdh(dtls_context_t *ctx, dtls_peer_t *peer,
+				   const dtls_ecdsa_key_t *key)
+{
+  /* The ASN.1 Integer representation of an 32 byte unsigned int could be
+   * 33 bytes long add space for that */
+  uint8 buf[DTLS_CV_LENGTH + 2];
+  uint8 *p;
+  uint32_t point_r[9];
+  uint32_t point_s[9];
+  dtls_hash_ctx hs_hash;
+  unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE];
+
+  /* ServerKeyExchange 
+   *
+   * Start message construction at beginning of buffer. */
+  p = buf;
+
+  /* sha256 */
+  dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_SHA256);
+  p += sizeof(uint8);
+
+  /* ecdsa */
+  dtls_int_to_uint8(p, TLS_EXT_SIG_HASH_ALGO_ECDSA);
+  p += sizeof(uint8);
+
+  copy_hs_hash(peer, &hs_hash);
+
+  dtls_hash_finalize(sha256hash, &hs_hash);
+
+  /* sign the ephemeral and its paramaters */
+  dtls_ecdsa_create_sig_hash(key->priv_key, DTLS_EC_KEY_SIZE,
+			     sha256hash, sizeof(sha256hash),
+			     point_r, point_s);
+
+  p = dtls_add_ecdsa_signature_elem(p, point_r, point_s);
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_VERIFY,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_finished(dtls_context_t *ctx, dtls_peer_t *peer,
+		   const unsigned char *label, size_t labellen)
+{
+  int length;
+  uint8 hash[DTLS_HMAC_MAX];
+  uint8 buf[DTLS_FIN_LENGTH];
+  dtls_hash_ctx hs_hash;
+  uint8 *p = buf;
+
+  copy_hs_hash(peer, &hs_hash);
+
+  length = dtls_hash_finalize(hash, &hs_hash);
+
+  dtls_prf(peer->handshake_params.tmp.master_secret,
+	   DTLS_MASTER_SECRET_LENGTH,
+	   label, labellen,
+	   PRF_LABEL(finished), PRF_LABEL_SIZE(finished), 
+	   hash, length,
+	   p, DTLS_FIN_LENGTH);
+
+  dtls_dsrv_hexdump_log(LOG_DEBUG, "server finished MAC", p, DTLS_FIN_LENGTH, 0);
+
+  p += DTLS_FIN_LENGTH;
+
+  assert(p - buf <= sizeof(buf));
+
+  return dtls_send_handshake_msg(ctx, peer, DTLS_HT_FINISHED,
+				 buf, p - buf);
+}
+
+static int
+dtls_send_client_hello(dtls_context_t *ctx, dtls_peer_t *peer,
+                       uint8 cookie[], size_t cookie_length) {
+  DBG("dtls_send_client_hello");
+  uint8 buf[DTLS_CH_LENGTH_MAX];
+  uint8 *p = buf;
+  uint8_t cipher_size;
+  uint8_t extension_size;
+  int psk;
+  int ecdsa;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+  dtls_tick_t now;
+
+  psk = is_psk_supported(ctx);
+  ecdsa = is_ecdsa_supported(ctx, 1);
+
+  cipher_size = 2 + ((ecdsa) ? 2 : 0) + ((psk) ? 2 : 0);
+  extension_size = (ecdsa) ? 2 + 6 + 6 + 8 + 6: 0;
+
+  if (cipher_size == 0) {
+    DBG("no cipher callbacks implemented");
+  }
+
+  dtls_int_to_uint16(p, DTLS_VERSION);
+  p += sizeof(uint16);
+
+  if (cookie_length > DTLS_COOKIE_LENGTH_MAX) {
+    WARN("the cookie is too long");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  if (cookie_length == 0) {
+    /* Set client random: First 4 bytes are the client's Unix timestamp,
+     * followed by 28 bytes of generate random data. */
+    dtls_ticks(&now);
+    dtls_int_to_uint32(&handshake->tmp.random.client, now / CLOCK_SECOND);
+    prng(handshake->tmp.random.client + sizeof(uint32),
+         DTLS_RANDOM_LENGTH - sizeof(uint32));
+  }
+  /* we must use the same Client Random as for the previous request */
+  memcpy(p, handshake->tmp.random.client, DTLS_RANDOM_LENGTH);
+  p += DTLS_RANDOM_LENGTH;
+
+  /* session id (length 0) */
+  dtls_int_to_uint8(p, 0);
+  p += sizeof(uint8);
+
+  /* cookie */
+  dtls_int_to_uint8(p, cookie_length);
+  p += sizeof(uint8);
+  if (cookie_length != 0) {
+    memcpy(p, cookie, cookie_length);
+    p += cookie_length;
+  }
+
+  /* add known cipher(s) */
+  dtls_int_to_uint16(p, cipher_size - 2);
+  p += sizeof(uint16);
+
+  if (ecdsa) {
+    dtls_int_to_uint16(p, TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+    p += sizeof(uint16);
+  }
+  if (psk) {
+    dtls_int_to_uint16(p, TLS_PSK_WITH_AES_128_CCM_8);
+    p += sizeof(uint16);
+  }
+
+  /* compression method */
+  dtls_int_to_uint8(p, 1);
+  p += sizeof(uint8);
+
+  dtls_int_to_uint8(p, TLS_COMPRESSION_NULL);
+  p += sizeof(uint8);
+
+  if (extension_size) {
+    /* length of the extensions */
+    dtls_int_to_uint16(p, extension_size - 2);
+    p += sizeof(uint16);
+  }
+
+  if (ecdsa) {
+    /* client certificate type extension */
+    dtls_int_to_uint16(p, TLS_EXT_CLIENT_CERIFICATE_TYPE);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    /* length of the list */
+    dtls_int_to_uint8(p, 1);
+    p += sizeof(uint8);
+
+    dtls_int_to_uint8(p, TLS_CERT_TYPE_OOB);
+    p += sizeof(uint8);
+
+    /* client certificate type extension */
+    dtls_int_to_uint16(p, TLS_EXT_SERVER_CERIFICATE_TYPE);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    /* length of the list */
+    dtls_int_to_uint8(p, 1);
+    p += sizeof(uint8);
+
+    dtls_int_to_uint8(p, TLS_CERT_TYPE_OOB);
+    p += sizeof(uint8);
+
+    /* elliptic_curves */
+    dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 4);
+    p += sizeof(uint16);
+
+    /* length of the list */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    dtls_int_to_uint16(p, TLS_EXT_ELLIPTIC_CURVES_SECP256R1);
+    p += sizeof(uint16);
+
+    /* ec_point_formats */
+    dtls_int_to_uint16(p, TLS_EXT_EC_POINT_FORMATS);
+    p += sizeof(uint16);
+
+    /* length of this extension type */
+    dtls_int_to_uint16(p, 2);
+    p += sizeof(uint16);
+
+    /* number of supported formats */
+    dtls_int_to_uint8(p, 1);
+    p += sizeof(uint8);
+
+    dtls_int_to_uint8(p, TLS_EXT_EC_POINT_FORMATS_UNCOMPRESSED);
+    p += sizeof(uint8);
+  }
+
+  assert(p - buf <= sizeof(buf));
+
+  if (cookie_length != 0)
+    clear_hs_hash(peer);
+
+  return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
+				      DTLS_HT_CLIENT_HELLO,
+				      buf, p - buf, cookie_length != 0);
+}
+
+static int
+check_server_hello(dtls_context_t *ctx, 
+		      dtls_peer_t *peer,
+		      uint8 *data, size_t data_length)
+{
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+
+  /* This function is called when we expect a ServerHello (i.e. we
+   * have sent a ClientHello).  We might instead receive a HelloVerify
+   * request containing a cookie. If so, we must repeat the
+   * ClientHello with the given Cookie.
+   */
+  if (data_length < DTLS_HS_LENGTH + DTLS_HS_LENGTH)
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+
+  update_hs_hash(peer, data, data_length);
+
+  /* FIXME: check data_length before accessing fields */
+
+  /* Get the server's random data and store selected cipher suite
+   * and compression method (like dtls_update_parameters().
+   * Then calculate master secret and wait for ServerHelloDone. When received,
+   * send ClientKeyExchange (?) and ChangeCipherSpec + ClientFinished. */
+    
+  /* check server version */
+  data += DTLS_HS_LENGTH;
+  data_length -= DTLS_HS_LENGTH;
+    
+  if (dtls_uint16_to_int(data) != DTLS_VERSION) {
+    DBG("unknown DTLS version");
+    return dtls_alert_fatal_create(DTLS_ALERT_PROTOCOL_VERSION);
+  }
+
+  data += sizeof(uint16);	      /* skip version field */
+  data_length -= sizeof(uint16);
+
+  /* store server random data */
+  memcpy(handshake->tmp.random.server, data, DTLS_RANDOM_LENGTH);
+  /* skip server random */
+  data += DTLS_RANDOM_LENGTH;
+  data_length -= DTLS_RANDOM_LENGTH;
+
+  SKIP_VAR_FIELD(data, data_length, uint8); /* skip session id */
+    
+  /* Check cipher suite. As we offer all we have, it is sufficient
+   * to check if the cipher suite selected by the server is in our
+   * list of known cipher suites. Subsets are not supported. */
+  handshake->cipher = dtls_uint16_to_int(data);
+  if (!known_cipher(ctx, handshake->cipher, 1)) {
+    DBG("unsupported cipher 0x%02x 0x%02x",
+	     data[0], data[1]);
+    return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
+  }
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16);
+
+  /* Check if NULL compression was selected. We do not know any other. */
+  if (dtls_uint8_to_int(data) != TLS_COMPRESSION_NULL) {
+    DBG("unsupported compression method 0x%02x", data[0]);
+    return dtls_alert_fatal_create(DTLS_ALERT_INSUFFICIENT_SECURITY);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  return dtls_check_tls_extension(peer, data, data_length, 0);
+
+error:
+  return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+}
+
+static int
+check_server_hello_verify_request(dtls_context_t *ctx,
+				  dtls_peer_t *peer,
+				  uint8 *data, size_t data_length)
+{
+  DBG("check_server_hello_verify_request");
+  dtls_hello_verify_t *hv;
+  int res;
+
+  if (data_length < DTLS_HS_LENGTH + DTLS_HV_LENGTH)
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+
+  hv = (dtls_hello_verify_t *)(data + DTLS_HS_LENGTH);
+
+  res = dtls_send_client_hello(ctx, peer, hv->cookie, hv->cookie_length);
+
+  if (res < 0)
+    WARN("cannot send ClientHello");
+
+  return res;
+}
+
+static int
+check_server_certificate(dtls_context_t *ctx, 
+			 dtls_peer_t *peer,
+			 uint8 *data, size_t data_length)
+{
+  DBG("check_server_certificate");
+  int err;
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+
+  update_hs_hash(peer, data, data_length);
+
+  assert(config->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+
+  data += DTLS_HS_LENGTH;
+
+  if (dtls_uint24_to_int(data) != 94) {
+    DBG("expect length of 94 bytes for server certificate message");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint24);
+
+  if (dtls_uint24_to_int(data) != 91) {
+    DBG("expect length of 91 bytes for certificate");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint24);
+
+  if (memcmp(data, cert_asn1_header, sizeof(cert_asn1_header))) {
+    DBG("got an unexpected Subject public key format");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(cert_asn1_header);
+
+  memcpy(config->keyx.ecdsa.other_pub_x, data,
+	 sizeof(config->keyx.ecdsa.other_pub_x));
+  data += sizeof(config->keyx.ecdsa.other_pub_x);
+
+  memcpy(config->keyx.ecdsa.other_pub_y, data,
+	 sizeof(config->keyx.ecdsa.other_pub_y));
+  data += sizeof(config->keyx.ecdsa.other_pub_y);
+
+  err = CALL(ctx, verify_ecdsa_key, &peer->session,
+	     config->keyx.ecdsa.other_pub_x,
+	     config->keyx.ecdsa.other_pub_y,
+	     sizeof(config->keyx.ecdsa.other_pub_x));
+  if (err < 0) {
+    WARN("The certificate was not accepted");
+    return err;
+  }
+
+  return 0;
+}
+
+static int
+check_server_key_exchange_ecdsa(dtls_context_t *ctx,
+				dtls_peer_t *peer,
+				uint8 *data, size_t data_length)
+{
+  DBG("check_server_key_exchange_ecdsa");
+
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+  int i;
+  unsigned char *result_r;
+  unsigned char *result_s;
+  unsigned char *key_params;
+
+  update_hs_hash(peer, data, data_length);
+
+  assert(config->cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+
+  data += DTLS_HS_LENGTH;
+
+  if (data_length < DTLS_HS_LENGTH + DTLS_SKEXEC_LENGTH) {
+    DBG("the package length does not match the expected");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  key_params = data;
+
+  if (dtls_uint8_to_int(data) != 3) {
+    DBG("Only named curves supported");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (dtls_uint16_to_int(data) != 23) {
+    DBG("secp256r1 supported");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16);
+
+  if (dtls_uint8_to_int(data) != 65) {
+    DBG("expected 65 bytes long public point");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (dtls_uint8_to_int(data) != 4) {
+    DBG("expected uncompressed public point");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  memcpy(config->keyx.ecdsa.other_eph_pub_x, data, sizeof(config->keyx.ecdsa.other_eph_pub_y));
+  data += sizeof(config->keyx.ecdsa.other_eph_pub_y);
+  data_length -= sizeof(config->keyx.ecdsa.other_eph_pub_y);
+
+  memcpy(config->keyx.ecdsa.other_eph_pub_y, data, sizeof(config->keyx.ecdsa.other_eph_pub_y));
+  data += sizeof(config->keyx.ecdsa.other_eph_pub_y);
+  data_length -= sizeof(config->keyx.ecdsa.other_eph_pub_y);
+
+
+  if (data_length < dtls_uint16_to_int(data)) {
+    DBG("signature length wrong");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint16);
+  data_length -= sizeof(uint16);
+
+  if (dtls_uint8_to_int(data) != 0x30) {
+    DBG("wrong ASN.1 struct, expected SEQUENCE");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (data_length < dtls_uint8_to_int(data)) {
+    DBG("signature length wrong");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  if (dtls_uint8_to_int(data) != 0x02) {
+    DBG("wrong ASN.1 struct, expected Integer");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  /* Sometimes these values have a leeding 0 byte */
+  result_r = data + i - DTLS_EC_KEY_SIZE;
+
+  data += i;
+  data_length -= i;
+
+  if (dtls_uint8_to_int(data) != 0x02) {
+    DBG("wrong ASN.1 struct, expected Integer");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  data_length -= sizeof(uint8);
+
+  /* Sometimes these values have a leeding 0 byte */
+  result_s = data + i - DTLS_EC_KEY_SIZE;
+
+  data += i;
+  data_length -= i;
+
+  i = dtls_ecdsa_verify_sig(config->keyx.ecdsa.other_pub_x, config->keyx.ecdsa.other_pub_y,
+			    sizeof(config->keyx.ecdsa.other_pub_x),
+			    config->tmp.random.client, DTLS_RANDOM_LENGTH,
+			    config->tmp.random.server, DTLS_RANDOM_LENGTH,
+			    key_params,
+			    1 + 2 + 1 + 1 + (2 * DTLS_EC_KEY_SIZE),
+			    result_r, result_s);
+
+  if (i < 0) {
+    DBG("wrong signature");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  return 0;
+}
+
+static int
+check_server_key_exchange_psk(dtls_context_t *ctx,
+			      dtls_peer_t *peer,
+			      uint8 *data, size_t data_length)
+{
+  DBG("check_server_key_exchange_psk");
+  dtls_handshake_parameters_t *config = &peer->handshake_params;
+  int len;
+
+  update_hs_hash(peer, data, data_length);
+
+  assert(config->cipher == TLS_PSK_WITH_AES_128_CCM_8);
+
+  data += DTLS_HS_LENGTH;
+
+  if (data_length < DTLS_HS_LENGTH + DTLS_SKEXECPSK_LENGTH_MIN) {
+    DBG("the package length does not match the expected");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  len = dtls_uint16_to_int(data);
+  data += sizeof(uint16);
+
+  if (len != data_length - DTLS_HS_LENGTH - sizeof(uint16)) {
+    WARN("the length of the server identity hint is worng");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  if (len > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
+    WARN("please use a smaller server identity hint");
+    return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+  }
+
+  config->keyx.psk.id_length = len;
+  memcpy(config->keyx.psk.identity, data, len);
+  return 0;
+}
+
+static int
+check_certificate_request(dtls_context_t *ctx, 
+			  dtls_peer_t *peer,
+			  uint8 *data, size_t data_length)
+{
+  DBG("check_certificate_request");
+  int i;
+  int auth_alg;
+  int sig_alg;
+  int hash_alg;
+
+  update_hs_hash(peer, data, data_length);
+
+  assert(peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
+
+  data += DTLS_HS_LENGTH;
+
+  if (data_length < DTLS_HS_LENGTH + 5) {
+    DBG("the package length does not match the expected");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  i = dtls_uint8_to_int(data);
+  data += sizeof(uint8);
+  if (i + 1 > data_length) {
+    DBG("the cerfificate types are too long");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  auth_alg = 0;
+  for (; i > 0 ; i -= sizeof(uint8)) {
+    if (dtls_uint8_to_int(data) == 64 && auth_alg == 0)
+      auth_alg = dtls_uint8_to_int(data);
+    data += sizeof(uint8);
+  }
+
+  if (auth_alg != 64) {
+    DBG("the request authentication algorithem is not supproted");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  i = dtls_uint16_to_int(data);
+  data += sizeof(uint16);
+  if (i + 1 > data_length) {
+    DBG("the signature and hash algorithm list is too long");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  hash_alg = 0;
+  sig_alg = 0;
+  for (; i > 0 ; i -= sizeof(uint16)) {
+    int current_hash_alg;
+    int current_sig_alg;
+
+    current_hash_alg = dtls_uint8_to_int(data);
+    data += sizeof(uint8);
+    current_sig_alg = dtls_uint8_to_int(data);
+    data += sizeof(uint8);
+
+    if (current_hash_alg == 4 && hash_alg == 0 && 
+        current_sig_alg == 3 && sig_alg == 0) {
+      hash_alg = current_hash_alg;
+      sig_alg = current_sig_alg;
+    }
+  }
+
+  if (hash_alg != 4 || sig_alg != 3) {
+    DBG("no supported hash and signature algorithem");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+
+  /* common names are ignored */
+
+  peer->handshake_params.do_client_auth = 1;
+  return 0;
+}
+
+static int
+check_server_hellodone(dtls_context_t *ctx, 
+		      dtls_peer_t *peer,
+		      uint8 *data, size_t data_length)
+{
+  int res;
+  const dtls_ecdsa_key_t *ecdsa_key;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+  dtls_security_parameters_t *security = &peer->security_params;
+  DBG("check_server_hellodone");
+  /* calculate master key, send CCS */
+
+  update_hs_hash(peer, data, data_length);
+
+  if (handshake->do_client_auth) {
+
+    res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
+    if (res < 0) {
+      DBG("no ecdsa certificate to send in certificate");
+      return res;
+    }
+
+    res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
+
+    if (res < 0) {
+      DBG("dtls_server_hello: cannot prepare Certificate record");
+      return res;
+    }
+  }
+
+  /* send ClientKeyExchange */
+  res = dtls_send_client_key_exchange(ctx, peer);
+
+  if (res < 0) {
+    DBG("cannot send KeyExchange message");
+    return res;
+  }
+
+  if (handshake->do_client_auth) {
+
+    res = dtls_send_certificate_verify_ecdh(ctx, peer, ecdsa_key);
+
+    if (res < 0) {
+      DBG("dtls_server_hello: cannot prepare Certificate record");
+      return res;
+    }
+  }
+
+  /* and switch cipher suite */
+  res = dtls_send_ccs(ctx, peer);
+  if (res < 0) {
+    DBG("cannot send CCS message");
+    return res;
+  }
+
+  res = calculate_key_block(ctx, handshake, security,
+			    &peer->session);
+  if (res < 0) {
+    return res;
+  }
+
+  res = init_cipher(handshake, security, peer->role);
+  if (res < 0) {
+    return res;
+  }
+
+  inc_uint(uint16, peer->epoch);
+  memset(peer->rseq, 0, sizeof(peer->rseq));
+
+  dtls_debug_keyblock(security);
+
+  /* Client Finished */
+  DBG("send Finished");
+  return dtls_send_finished(ctx, peer, PRF_LABEL(client), PRF_LABEL_SIZE(client));
+}
+
+static int
+decrypt_verify(dtls_peer_t *peer,
+	       uint8 *packet, size_t length,
+	       uint8 **cleartext, size_t *clen) {
+  int ok = 0;
+  dtls_security_parameters_t *security = &peer->security_params;
+  
+  *cleartext = (uint8 *)packet + sizeof(dtls_record_header_t);
+  *clen = length - sizeof(dtls_record_header_t);
+
+  if (security->cipher == TLS_NULL_WITH_NULL_NULL) {
+    /* no cipher suite selected */
+    return 1;
+  } else {			/* TLS_PSK_WITH_AES_128_CCM_8 */   
+    dtls_cipher_context_t *cipher_context;
+    /** 
+     * length of additional_data for the AEAD cipher which consists of
+     * seq_num(2+6) + type(1) + version(2) + length(2)
+     */
+#define A_DATA_LEN 13
+    unsigned char nonce[DTLS_CCM_BLOCKSIZE];
+    unsigned char A_DATA[A_DATA_LEN];
+    long int len;
+
+
+    if (*clen < 16)		/* need at least IV and MAC */
+      return -1;
+
+    memset(nonce, 0, DTLS_CCM_BLOCKSIZE);
+    memcpy(nonce, dtls_kb_remote_iv(security, peer->role),
+	   dtls_kb_iv_size(security, peer->role));
+
+    /* read epoch and seq_num from message */
+    memcpy(nonce + dtls_kb_iv_size(security, peer->role), *cleartext, 8);
+    *cleartext += 8;
+    *clen -= 8;
+
+    cipher_context = security->read_cipher;
+    
+    if (!cipher_context) {
+      WARN("no read_cipher available!");
+      return 0;
+    }
+
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "nonce", nonce, DTLS_CCM_BLOCKSIZE, 0);
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "key",
+			  dtls_kb_remote_write_key(security, peer->role),
+			  dtls_kb_key_size(security, peer->role), 0);
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "ciphertext", *cleartext, *clen, 0);
+
+    /* re-use N to create additional data according to RFC 5246, Section 6.2.3.3:
+     * 
+     * additional_data = seq_num + TLSCompressed.type +
+     *                   TLSCompressed.version + TLSCompressed.length;
+     */
+    memcpy(A_DATA, &DTLS_RECORD_HEADER(packet)->epoch, 8); /* epoch and seq_num */
+    memcpy(A_DATA + 8,  &DTLS_RECORD_HEADER(packet)->content_type, 3); /* type and version */
+    dtls_int_to_uint16(A_DATA + 11, *clen - 8); /* length without nonce_explicit */
+
+    len = dtls_decrypt(cipher_context, *cleartext, *clen, *cleartext, nonce,
+		       A_DATA, A_DATA_LEN);
+
+    ok = len >= 0;
+    if (!ok)
+      WARN("decryption failed");
+    else {
+#ifndef NDEBUG
+      printf("decrypt_verify(): found %ld bytes cleartext", len);
+#endif
+      *clen = len;
+    }
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "cleartext", *cleartext, *clen, 0);
+  }
+
+  return ok;
+}
+
+static int
+dtls_send_hello_request(dtls_context_t *ctx, dtls_peer_t *peer)
+{
+  return dtls_send_handshake_msg_hash(ctx, peer, &peer->session,
+				      DTLS_HT_HELLO_REQUEST,
+				      NULL, 0, 0);
+}
+
+int
+dtls_renegotiate(dtls_context_t *ctx, const session_t *dst)
+{
+  dtls_peer_t *peer = NULL;
+  int err;
+
+  peer = dtls_get_peer(ctx, dst);
+
+  if (!peer) {
+    return -1;
+  }
+  if (peer->state != DTLS_STATE_CONNECTED)
+    return -1;
+
+  if (peer->role == DTLS_CLIENT) {
+    /* send ClientHello with empty Cookie */
+    err = dtls_send_client_hello(ctx, peer, NULL, 0);
+    if (err < 0)
+      WARN("cannot send ClientHello");
+    else
+      peer->state = DTLS_STATE_CLIENTHELLO;
+    return err;
+  } else if (peer->role == DTLS_SERVER) {
+    return dtls_send_hello_request(ctx, peer);
+  }
+
+  return -1;
+}
+
+static int
+handle_handshake(dtls_context_t *ctx, dtls_peer_t *peer, session_t *session,
+		 const dtls_peer_type role, const dtls_state_t state,
+		 uint8 *record_header, uint8 *data, size_t data_length) {
+
+  int err = 0;
+
+  if (data_length < DTLS_HS_LENGTH) {
+    WARN("handshake message too short");
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+  }
+
+  if (!peer && data[0] != DTLS_HT_CLIENT_HELLO) {
+    WARN("If there is no peer only ClientHello is allowed");
+    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
+  }
+  /* The following switch construct handles the given message with
+   * respect to the current internal state for this peer. In case of
+   * error, it is left with return 0. */
+
+  switch (data[0]) {
+
+  /************************************************************************
+   * Client states
+   ************************************************************************/
+  case DTLS_HT_HELLO_VERIFY_REQUEST:
+
+    DBG("DTLS_HT_HELLO_VERIFY_REQUEST");
+    if (state != DTLS_STATE_CLIENTHELLO) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_server_hello_verify_request(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_server_hello_verify_request err: %i", err);
+      return err;
+    }
+
+    break;
+  case DTLS_HT_SERVER_HELLO:
+
+    DBG("DTLS_HT_SERVER_HELLO");
+    if (state != DTLS_STATE_CLIENTHELLO) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_server_hello(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_server_hello err: %i", err);
+      return err;
+    }
+    if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8)
+      peer->state = DTLS_STATE_WAIT_SERVERCERTIFICATE;
+    else
+      peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
+    /* update_hs_hash(peer, data, data_length); */
+
+    break;
+
+  case DTLS_HT_CERTIFICATE:
+    DBG("DTLS_HT_CERTIFICATE");
+
+    if ((role == DTLS_CLIENT && state != DTLS_STATE_WAIT_SERVERCERTIFICATE) ||
+        (role == DTLS_SERVER && state != DTLS_STATE_WAIT_CLIENTCERTIFICATE)) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+    err = check_server_certificate(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_server_certificate err: %i", err);
+      return err;
+    }
+    if (role == DTLS_CLIENT) {
+      peer->state = DTLS_STATE_WAIT_SERVERKEYEXCHANGE;
+    } else if (role == DTLS_SERVER){
+      peer->state = DTLS_STATE_WAIT_CLIENTKEYEXCHANGE;
+    }
+    /* update_hs_hash(peer, data, data_length); */
+
+    break;
+
+  case DTLS_HT_SERVER_KEY_EXCHANGE:
+
+    DBG("DTLS_HT_SERVER_KEY_EXCHANGE");
+
+    if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8) {
+      DBG("TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8");
+      if (state != DTLS_STATE_WAIT_SERVERKEYEXCHANGE) {
+        return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+      }
+      err = check_server_key_exchange_ecdsa(ctx, peer, data, data_length);
+    } else if (peer->handshake_params.cipher == TLS_PSK_WITH_AES_128_CCM_8) {
+      DBG("TLS_PSK_WITH_AES_128_CCM_8");
+      if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
+        return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+      }
+      err = check_server_key_exchange_psk(ctx, peer, data, data_length);
+    } else {
+      assert(0);
+    }
+
+    if (err < 0) {
+      WARN("error in check_server_key_exchange err: %i", err);
+      return err;
+    }
+    peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
+    /* update_hs_hash(peer, data, data_length); */
+    DBG("Set state to DTLS_STATE_WAIT_SERVERHELLODONE");
+    break;
+
+  case DTLS_HT_SERVER_HELLO_DONE:
+
+    DBG("DTLS_HT_SERVER_HELLO_DONE");
+    if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_server_hellodone(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_server_hellodone err: %i", err);
+      return err;
+    }
+    peer->state = DTLS_STATE_WAIT_SERVERFINISHED;
+    /* update_hs_hash(peer, data, data_length); */
+
+    break;
+
+  case DTLS_HT_CERTIFICATE_REQUEST:
+
+    DBG("DTLS_HT_CERTIFICATE_REQUEST");
+    if (state != DTLS_STATE_WAIT_SERVERHELLODONE) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_certificate_request(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_certificate_request err: %i", err);
+      return err;
+    }
+
+    break;
+
+  case DTLS_HT_FINISHED:
+    /* expect a Finished message from server */
+
+    DBG("DTLS_HT_FINISHED");
+    if ((role == DTLS_CLIENT && state != DTLS_STATE_WAIT_SERVERFINISHED) ||
+        (role == DTLS_SERVER && state != DTLS_STATE_WAIT_FINISHED)) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_finished(ctx, peer, record_header, data, data_length);
+    if (err < 0) {
+      WARN("error in check_finished err: %i", err);
+      return err;
+    }
+    if (role == DTLS_SERVER) {
+      /* send ServerFinished */
+      update_hs_hash(peer, data, data_length);
+
+      if (dtls_send_finished(ctx, peer, PRF_LABEL(server),
+			     PRF_LABEL_SIZE(server)) > 0) {
+        peer->state = DTLS_STATE_CONNECTED;
+      } else  if (role == DTLS_CLIENT) {
+        WARN("sending server Finished failed");
+      }
+    } else {
+      peer->state = DTLS_STATE_CONNECTED;
+    }
+
+    break;
+
+  /************************************************************************
+   * Server states
+   ************************************************************************/
+
+  case DTLS_HT_CLIENT_KEY_EXCHANGE:
+    /* handle ClientHello, update msg and msglen and goto next if not finished */
+
+    DBG("DTLS_HT_CLIENT_KEY_EXCHANGE");
+
+    if (state != DTLS_STATE_WAIT_CLIENTKEYEXCHANGE) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_client_keyexchange(ctx, &peer->handshake_params, data, data_length);
+    if (err < 0) {
+      WARN("error in check_client_keyexchange err: %i", err);
+      return err;
+    }
+    update_hs_hash(peer, data, data_length);
+
+    if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 &&
+	ctx && ctx->h && ctx->h->verify_ecdsa_key)
+      peer->state = DTLS_STATE_WAIT_CERTIFICATEVERIFY;
+    else
+      peer->state = DTLS_STATE_WAIT_CLIENTCHANGECIPHERSPEC;
+    break;
+
+  case DTLS_HT_CERTIFICATE_VERIFY:
+
+    DBG("DTLS_HT_CERTIFICATE_VERIFY");
+    if (state != DTLS_STATE_WAIT_CERTIFICATEVERIFY) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    err = check_client_certificate_verify(ctx, peer, data, data_length);
+    if (err < 0) {
+      WARN("error in check_client_certificate_verify err: %i", err);
+      return err;
+    }
+
+    update_hs_hash(peer, data, data_length);
+    peer->state = DTLS_STATE_WAIT_CLIENTCHANGECIPHERSPEC;
+    break;
+
+  case DTLS_HT_CLIENT_HELLO:
+    /* At this point, we have a good relationship with this peer. This
+     * state is left for re-negotiation of key material. */
+    
+    DBG("DTLS_HT_CLIENT_HELLO");
+    if ((peer && state != DTLS_STATE_CONNECTED) ||
+	(!peer && state != DTLS_STATE_WAIT_CLIENTHELLO)) {
+      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+    }
+
+    /* When no DTLS state exists for this peer, we only allow a
+       Client Hello message with
+
+       a) a valid cookie, or
+       b) no cookie.
+
+       Anything else will be rejected. Fragementation is not allowed
+       here as it would require peer state as well.
+    */
+    err = dtls_verify_peer(ctx, peer, session, record_header, data,
+			   data_length);
+    if (err < 0) {
+      WARN("error in dtls_verify_peer err: %i", err);
+      return err;
+    }
+
+    if (err > 0) {
+      DBG("server hello verify was sent");
+      return err;
+    }
+
+    if (!peer) {
+
+      /* msg contains a Client Hello with a valid cookie, so we can
+       * safely create the server state machine and continue with
+       * the handshake. */
+      peer = dtls_new_peer(session);
+      if (!peer) {
+        DBG("cannot create peer");
+        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
+      }
+      peer->role = DTLS_SERVER;
+
+      /* Initialize record sequence number to 1 for new peers. The first
+       * record with sequence number 0 is a stateless Hello Verify Request.
+       */
+      peer->rseq[5] = 1;
+      dtls_add_peer(ctx, peer);
+    }
+
+    clear_hs_hash(peer);
+
+    /* First negotiation step: check for PSK
+     *
+     * Note that we already have checked that msg is a Handshake
+     * message containing a ClientHello. dtls_get_cipher() therefore
+     * does not check again.
+     */
+    err = dtls_update_parameters(ctx, peer, data, data_length);
+    if (err < 0) {
+
+      WARN("error updating security parameters");
+      return err;
+    }
+
+    /* update finish MAC */
+    update_hs_hash(peer, data, data_length);
+
+    err = dtls_send_server_hello_msgs(ctx, peer);
+    if (err < 0) {
+      return err;
+    }
+    if (peer->handshake_params.cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 &&
+	ctx && ctx->h && ctx->h->verify_ecdsa_key)
+      peer->state = DTLS_STATE_WAIT_CLIENTCERTIFICATE;
+    else
+      peer->state = DTLS_STATE_WAIT_CLIENTKEYEXCHANGE;
+
+    /* after sending the ServerHelloDone, we expect the
+     * ClientKeyExchange (possibly containing the PSK id),
+     * followed by a ChangeCipherSpec and an encrypted Finished.
+     */
+
+    break;
+
+  case DTLS_HT_HELLO_REQUEST:
+
+    DBG("DTLS_HT_HELLO_REQUEST");
+    if (state != DTLS_STATE_CONNECTED) {
+      /* we should just ignore such packages when in handshake */
+      return 0;
+    }
+
+    /* send ClientHello with empty Cookie */
+    err = dtls_send_client_hello(ctx, peer, NULL, 0);
+    if (err < 0)
+      WARN("cannot send ClientHello");
+    else
+      peer->state = DTLS_STATE_CLIENTHELLO;
+
+    return err;
+  default:
+    DBG("unhandled message %d", data[0]);
+    return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+  }
+
+  return err;
+}
+
+static int
+handle_ccs(dtls_context_t *ctx, dtls_peer_t *peer, 
+	   uint8 *record_header, uint8 *data, size_t data_length)
+{
+  int err;
+  dtls_handshake_parameters_t *handshake = &peer->handshake_params;
+  dtls_security_parameters_t *security = &peer->security_params;
+
+  /* A CCS message is handled after a KeyExchange message was
+   * received from the client. When security parameters have been
+   * updated successfully and a ChangeCipherSpec message was sent
+   * by ourself, the security context is switched and the record
+   * sequence number is reset. */
+  
+  if (!peer || peer->state != DTLS_STATE_WAIT_CLIENTCHANGECIPHERSPEC) {
+    WARN("expected ChangeCipherSpec during handshake");
+    return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
+  }
+
+  if (data_length < 1 || data[0] != 1)
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+
+  /* send change cipher spec message and switch to new configuration */
+  err = dtls_send_ccs(ctx, peer);
+  if (err < 0) {
+    WARN("cannot send CCS message");
+    return err;
+  }
+
+  err = calculate_key_block(ctx, handshake, security,
+			    &peer->session);
+  if (err < 0) {
+    return err;
+  }
+
+  err = init_cipher(handshake, security, peer->role);
+  if (err < 0) {
+    return err;
+  }
+  
+  inc_uint(uint16, peer->epoch);
+  memset(peer->rseq, 0, sizeof(peer->rseq));
+  
+  peer->state = DTLS_STATE_WAIT_FINISHED;
+
+  dtls_debug_keyblock(security);
+
+  return 0;
+}  
+
+/** 
+ * Handles incoming Alert messages. This function returns \c 1 if the
+ * connection should be closed and the peer is to be invalidated.
+ */
+static int
+handle_alert(dtls_context_t *ctx, dtls_peer_t *peer, 
+	     uint8 *record_header, uint8 *data, size_t data_length) {
+  int free_peer = 0;		/* indicates whether to free peer */
+
+  if (data_length < 2)
+    return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
+
+  INFO("** Alert: level %d, description %d", data[0], data[1]);
+
+  if (!peer) {
+    WARN("got an alert for an unknown peer, we probably already removed it, ignore it");
+    return 0;
+  }
+
+  /* The peer object is invalidated for FATAL alerts and close
+   * notifies. This is done in two steps.: First, remove the object
+   * from our list of peers. After that, the event handler callback is
+   * invoked with the still existing peer object. Finally, the storage
+   * used by peer is released.
+   */
+  if (data[0] == DTLS_ALERT_LEVEL_FATAL || data[1] == DTLS_ALERT_CLOSE_NOTIFY) {
+    DBG("%d invalidate peer", data[1]);
+    
+#ifndef WITH_CONTIKI
+    HASH_DEL_PEER(ctx->peers, peer);
+#else /* WITH_CONTIKI */
+    list_remove(ctx->peers, peer);
+
+#ifndef NDEBUG
+    PRINTF("removed peer [");
+    PRINT6ADDR(&peer->session.addr);
+    PRINTF("]:%d", uip_ntohs(peer->session.port));
+#endif
+#endif /* WITH_CONTIKI */
+
+    free_peer = 1;
+
+  }
+
+  (void)CALL(ctx, event, &peer->session, 
+	     (dtls_alert_level_t)data[0], (unsigned short)data[1]);
+  switch (data[1]) {
+  case DTLS_ALERT_CLOSE_NOTIFY:
+    /* If state is DTLS_STATE_CLOSING, we have already sent a
+     * close_notify so, do not send that again. */
+    if (peer->state != DTLS_STATE_CLOSING) {
+      peer->state = DTLS_STATE_CLOSING;
+      dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE_NOTIFY);
+    } else
+      peer->state = DTLS_STATE_CLOSED;
+    break;
+  default:
+    ;
+  }
+  
+  if (free_peer) {
+    dtls_stop_retransmission(ctx, peer);
+    dtls_destory_peer(ctx, peer, 0);
+  }
+
+  return free_peer;
+}
+
+static int dtls_alert_send_from_err(dtls_context_t *ctx, dtls_peer_t *peer,
+				    session_t *session, int err)
+{
+  int level;
+  int desc;
+
+  if (err < -(1 << 8) && err > -(3 << 8)) {
+    level = ((-err) & 0xff00) >> 8;
+    desc = (-err) & 0xff;
+    if (!peer) {
+      peer = dtls_get_peer(ctx, session);
+    }
+    if (peer) {
+      return dtls_alert(ctx, peer, level, desc);
+    }
+  } else if (err == -1) {
+    if (!peer) {
+      peer = dtls_get_peer(ctx, session);
+    }
+    if (peer) {
+      return dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_INTERNAL_ERROR);
+    }
+  }
+  return -1;
+}
+
+/** 
+ * Handles incoming data as DTLS message from given peer.
+ */
+int
+dtls_handle_message(dtls_context_t *ctx, 
+		    session_t *session,
+		    uint8 *msg, int msglen) {
+  dtls_peer_t *peer = NULL;
+  unsigned int rlen;		/* record length */
+  uint8 *data; 			/* (decrypted) payload */
+  size_t data_length;		/* length of decrypted payload 
+				   (without MAC and padding) */
+  int err;
+
+  /* check if we have DTLS state for addr/port/ifindex */
+  peer = dtls_get_peer(ctx, session);
+
+  if (!peer) {
+    DBG("dtls_handle_message: PEER NOT FOUND");
+    dtls_dsrv_log_addr(LOG_DEBUG, "peer addr", session);
+  } else {
+    DBG("dtls_handle_message: FOUND PEER");
+  }
+
+  /* FIXME: check sequence number of record and drop message if the
+   * number is not exactly the last number that we have responded to + 1. 
+   * Otherwise, stop retransmissions for this specific peer and 
+   * continue processing. */
+  if (peer) {
+    dtls_stop_retransmission(ctx, peer);
+  }
+
+  while ((rlen = is_record(msg,msglen))) {
+    dtls_peer_type role;
+    dtls_state_t state;
+
+    DBG("got packet %d (%d bytes)", msg[0], rlen);
+    if (peer) {
+      /* skip packet if it is from a different epoch */
+      if (memcmp(DTLS_RECORD_HEADER(msg)->epoch,
+		 peer->epoch, sizeof(uint16)) != 0)
+        goto next;
+
+      if (!decrypt_verify(peer, msg, rlen, &data, &data_length)) {
+        INFO("decrypt_verify() failed");
+        goto next;
+      }
+      role = peer->role;
+      state = peer->state;
+    } else {
+      /* is_record() ensures that msg contains at least a record header */
+      data = msg + DTLS_RH_LENGTH;
+      data_length = rlen - DTLS_RH_LENGTH;
+      state = DTLS_STATE_WAIT_CLIENTHELLO;
+      role = DTLS_SERVER;
+    }
+
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "receive header", msg,
+			  sizeof(dtls_record_header_t), 1);
+    dtls_dsrv_hexdump_log(LOG_DEBUG, "receive unencrypted", data, data_length, 1);
+
+    /* Handle received record according to the first byte of the
+     * message, i.e. the subprotocol. We currently do not support
+     * combining multiple fragments of one type into a single
+     * record. */
+
+    switch (msg[0]) {
+
+    case DTLS_CT_CHANGE_CIPHER_SPEC:
+      err = handle_ccs(ctx, peer, msg, data, data_length);
+      if (err < 0) {
+	WARN("error while handling ChangeCipherSpec package");
+	dtls_alert_send_from_err(ctx, peer, session, err);
+	return err;
+      }
+      break;
+
+    case DTLS_CT_ALERT:
+      err = handle_alert(ctx, peer, msg, data, data_length);
+      if (err < 0) {
+        WARN("received wrong package");
+	/* handle alert has invalidated peer */
+	peer = NULL;
+	return err;
+      }
+      break;
+
+    case DTLS_CT_HANDSHAKE:
+      err = handle_handshake(ctx, peer, session, role, state, msg, data, data_length);
+      DBG("Returned from handle_handshake: %d",err);
+      if (err < 0) {
+	     WARN("error while handling handshake package");
+	     dtls_alert_send_from_err(ctx, peer, session, err);
+	     return err;
+      }
+      if (peer && peer->state == DTLS_STATE_CONNECTED) {
+        DBG("DTLS_STATE_CONNECTED");
+		/* stop retransmissions */
+		dtls_stop_retransmission(ctx, peer);
+		CALL(ctx, event, &peer->session, 0, DTLS_EVENT_CONNECTED);
+      }
+      break;
+
+    case DTLS_CT_APPLICATION_DATA:
+      INFO("** application data:");
+      if (!peer) {
+        WARN("no peer available, send an alert");
+        dtls_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_UNEXPECTED_MESSAGE);
+        return -1;
+      }
+      CALL(ctx, read, &peer->session, data, data_length);
+      break;
+    default:
+      INFO("dropped unknown message of type %d",msg[0]);
+    }
+
+  next:
+    /* advance msg by length of ciphertext */
+    msg += rlen;
+    msglen -= rlen;
+  }
+  DBG("Leaving dtls_handle_message");
+  return 0;
+}
+
+dtls_context_t *
+dtls_new_context(void *app_data) {
+  dtls_context_t *c;
+  dtls_tick_t now;
+#ifndef WITH_CONTIKI
+  FILE *urandom = fopen("/dev/urandom", "r");
+  unsigned char buf[sizeof(unsigned long)];
+#endif /* WITH_CONTIKI */
+
+  dtls_ticks(&now);
+#ifdef WITH_CONTIKI
+  /* FIXME: need something better to init PRNG here */
+  prng_init(now);
+#else /* WITH_CONTIKI */
+/*
+  if (!urandom) {
+    DBG("cannot initialize PRNG");
+    return NULL;
+  }
+
+  if (fread(buf, 1, sizeof(buf), urandom) != sizeof(buf)) {
+    dsrv_log(LOG_EMERG, "cannot initialize PRNG");
+    return NULL;
+  }
+
+  fclose(urandom);
+  prng_init((unsigned long)*buf);
+  */
+  // just randomly flip some bits
+  unsigned long mask = 0x0000;
+  for(int i=0; i<sizeof(buf); i++) {
+     // XXX need to flip some bits
+  }
+  prng_init((unsigned long)*buf);
+#endif /* WITH_CONTIKI */
+
+  c = &the_dtls_context;
+
+  memset(c, 0, sizeof(dtls_context_t));
+  c->app = app_data;
+  
+  LIST_STRUCT_INIT(c, sendqueue);
+
+#ifdef WITH_CONTIKI
+  LIST_STRUCT_INIT(c, peers);
+  /* LIST_STRUCT_INIT(c, key_store); */
+  
+  process_start(&dtls_retransmit_process, (char *)c);
+  PROCESS_CONTEXT_BEGIN(&dtls_retransmit_process);
+  /* the retransmit timer must be initialized to some large value */
+  etimer_set(&c->retransmit_timer, 0xFFFF);
+  PROCESS_CONTEXT_END(&coap_retransmit_process);
+#endif /* WITH_CONTIKI */
+
+  if (prng(c->cookie_secret, DTLS_COOKIE_SECRET_LENGTH))
+    c->cookie_secret_age = now;
+  else 
+    goto error;
+  
+  return c;
+
+ error:
+  DBG("cannot create DTLS context");
+  if (c)
+    dtls_free_context(c);
+  return NULL;
+}
+
+void dtls_free_context(dtls_context_t *ctx) {
+  dtls_peer_t *p;
+  
+#ifndef WITH_CONTIKI
+  dtls_peer_t *tmp;
+
+  if (ctx->peers) {
+    HASH_ITER(hh, ctx->peers, p, tmp) {
+      dtls_destory_peer(ctx, p, 1);
+    }
+  }
+#else /* WITH_CONTIKI */
+  for (p = list_head(ctx->peers); p; p = list_item_next(p))
+    dtls_destory_peer(ctx, p, 1);
+#endif /* WITH_CONTIKI */
+}
+
+int
+dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
+  int res;
+
+  assert(peer);
+  if (!peer)
+    return -1;
+
+  /* check if the same peer is already in our list */
+  if (peer == dtls_get_peer(ctx, &peer->session)) {
+    DBG("found peer, try to re-connect");
+    /* FIXME: send HelloRequest if we are server, 
+       ClientHello with good cookie if client */
+    return 0;
+  }
+    
+  /* set peer role to server: */
+  peer->role = DTLS_CLIENT;
+
+  dtls_add_peer(ctx, peer);
+
+  /* send ClientHello with empty Cookie */
+  res = dtls_send_client_hello(ctx, peer, NULL, 0);
+  if (res < 0)
+    WARN("cannot send ClientHello");
+  else 
+    peer->state = DTLS_STATE_CLIENTHELLO;
+
+  return res;
+}
+
+int
+dtls_connect(dtls_context_t *ctx, const session_t *dst) {
+  dtls_peer_t *peer;
+
+  peer = dtls_get_peer(ctx, dst);
+  
+  if (!peer)
+    peer = dtls_new_peer(dst);
+
+  if (!peer) {
+    DBG("cannot create new peer");
+    return -1;
+  }
+
+  return dtls_connect_peer(ctx, peer);
+}
+
+static void
+dtls_retransmit(dtls_context_t *context, netq_t *node) {
+  if (!context || !node)
+    return;
+
+  /* re-initialize timeout when maximum number of retransmissions are not reached yet */
+  if (node->retransmit_cnt < DTLS_DEFAULT_MAX_RETRANSMIT) {
+      unsigned char sendbuf[DTLS_MAX_BUF];
+      size_t len = sizeof(sendbuf);
+
+      node->retransmit_cnt++;
+      node->t += (node->timeout << node->retransmit_cnt);
+      netq_insert_node((netq_t **)context->sendqueue, node);
+      
+      DBG("** retransmit packet");
+      
+      if (dtls_prepare_record(node->peer, DTLS_CT_HANDSHAKE, 
+			      (uint8 **)&(node->data), &(node->length), 1,
+			      sendbuf, &len) > 0) {
+
+	dtls_dsrv_hexdump_log(LOG_DEBUG, "retransmit header", sendbuf,
+			      sizeof(dtls_record_header_t), 1);
+	dtls_dsrv_hexdump_log(LOG_DEBUG, "retransmit unencrypted", node->data,
+			      node->length, 1);
+
+	(void)CALL(context, write, &node->peer->session, sendbuf, len);
+      }
+      return;
+  }
+
+  /* no more retransmissions, remove node from system */
+  
+  DBG("** removed transaction");
+
+  /* And finally delete the node */
+  netq_node_free(node);
+}
+
+static void
+dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer) {
+  void *node;
+  node = list_head((list_t)context->sendqueue); 
+
+  while (node) {
+    if (dtls_session_equals(&((netq_t *)node)->peer->session,
+			    &peer->session)) {
+      void *tmp = node;
+      node = list_item_next(node);
+      list_remove((list_t)context->sendqueue, tmp);
+      netq_node_free((netq_t *)tmp);
+    } else
+      node = list_item_next(node);    
+  }
+}
+
+void
+dtls_check_retransmit(dtls_context_t *context, clock_time_t *next) {
+  dtls_tick_t now;
+  netq_t *node = netq_head((netq_t **)context->sendqueue);
+
+  dtls_ticks(&now);
+  while (node && node->t <= now) {
+    netq_pop_first((netq_t **)context->sendqueue);
+    dtls_retransmit(context, node);
+    node = netq_head((netq_t **)context->sendqueue);
+  }
+
+  if (next && node)
+    *next = node->t;
+}
+
+#ifdef WITH_CONTIKI
+/*---------------------------------------------------------------------------*/
+/* message retransmission */
+/*---------------------------------------------------------------------------*/
+PROCESS_THREAD(dtls_retransmit_process, ev, data)
+{
+  clock_time_t now;
+  netq_t *node;
+
+  PROCESS_BEGIN();
+
+  DBG("Started DTLS retransmit process\r");
+
+  while(1) {
+    PROCESS_YIELD();
+    if (ev == PROCESS_EVENT_TIMER) {
+      if (etimer_expired(&the_dtls_context.retransmit_timer)) {
+	
+	node = list_head(the_dtls_context.sendqueue);
+	
+	now = clock_time();
+	while (node && node->t <= now) {
+	  dtls_retransmit(&the_dtls_context, list_pop(the_dtls_context.sendqueue));
+	  node = list_head(the_dtls_context.sendqueue);
+	}
+
+	/* need to set timer to some value even if no nextpdu is available */
+	etimer_set(&the_dtls_context.retransmit_timer, 
+		   node ? node->t - now : 0xFFFF);
+      } 
+    }
+  }
+  
+  PROCESS_END();
+}
+#endif /* WITH_CONTIKI */