text: /* dtls -- a very basic DTLS implementation
   *
   * Copyright (C) 2011--2012,2014 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 "tinydtls.h"
  #include "dtls_config.h"
  #include "dtls_time.h"
  
  #include <stdio.h>
  #include <stdlib.h>
  #ifdef HAVE_ASSERT_H
  #include <assert.h>
  #endif
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
  
  #include <stdlib.h>
  #include "uthash.h"
  #endif /* WITH_CONTIKI */
  #include "wilddog.h"
  #include "debug.h"
  #include "numeric.h"
  #include "netq.h"
  #include "dtls.h"
  
  #include "alert.h"
  #include "session.h"
  #include "prng.h"
  
  #ifdef WILDDOG_SELFTEST 
  #include "test_lib.h"
  #endif
  
  #ifdef WITH_SHA256
  #  include "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)
  
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
  #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 + 1 + 1 + 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 */
  };
  
  #ifdef WITH_CONTIKI
  PROCESS(dtls_retransmit_process, "DTLS retransmit process");
  
  static dtls_context_t the_dtls_context;
  
  static inline dtls_context_t *
  malloc_context() {
    return &the_dtls_context;
  }
  
  static inline void
  free_context(dtls_context_t *context) {
  }
  
  #else /* WITH_CONTIKI */
  
  static inline dtls_context_t *
  malloc_context() {
    return (dtls_context_t *)malloc(sizeof(dtls_context_t));
  }
  
  static inline void
  free_context(dtls_context_t *context) {
    free(context);
  }
  #endif
  
  void
  dtls_init() {
    dtls_clock_init();
    crypto_init();
    netq_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)
  
  static int
  dtls_send_multi(dtls_context_t *ctx, dtls_peer_t *peer,
  		dtls_security_parameters_t *security , session_t *session,
  		unsigned char type, uint8 *buf_array[],
  		size_t buf_len_array[], size_t buf_array_len);
  
  /** 
   * 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) {
    return dtls_send_multi(ctx, peer, dtls_security_params(peer), &peer->session,
  			 type, &buf, &buflen, 1);
  }
  
  /**
   * 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;
  
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined (WILDDOG_PORT_TYPE_MXCHIP)
    HASH_FIND_PEER(ctx->peers, session, p);
  #else /* WITH_CONTIKI */
    for (p = list_head((void **)&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) {
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
    HASH_ADD_PEER(ctx->peers, session, peer);
  #else /* WITH_CONTIKI */
    list_add((void **)(&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, size_t 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, size_t 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, size_t msglen) {
    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 security.
   * \return pointer to the next byte after the written header.
   * The length will be set to 0 and has to be changed before sending.
   */ 
  static inline uint8 *
  dtls_set_record_header(uint8 type, dtls_security_parameters_t *security,
  		       uint8 *buf) {
    
    dtls_int_to_uint8(buf, type);
    buf += sizeof(uint8);
  
    dtls_int_to_uint16(buf, DTLS_VERSION);
    buf += sizeof(uint16);
  
    if (security) {
      dtls_int_to_uint16(buf, security->epoch);
      buf += sizeof(uint16);
  
      dtls_int_to_uint48(buf, security->rseq);
      buf += sizeof(uint48);
  
      /* increment record sequence counter by 1 */
      security->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 && peer->handshake_params) {
      /* and copy the result to buf */
      dtls_int_to_uint16(buf, peer->handshake_params->hs_state.mseq_s);
  
      /* increment handshake message sequence counter by 1 */
      peer->handshake_params->hs_state.mseq_s++;
    } 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
  };
  
  /** returns true if the cipher matches TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */
  static inline int is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(dtls_cipher_t cipher)
  {
  #ifdef DTLS_ECC
    return cipher == TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8;
  #else
    return 0;
  #endif /* DTLS_ECC */
  }
  
  /** returns true if the cipher matches TLS_PSK_WITH_AES_128_CCM_8 */
  static inline int is_tls_psk_with_aes_128_ccm_8(dtls_cipher_t cipher)
  {
  #ifdef DTLS_PSK
    return cipher == TLS_PSK_WITH_AES_128_CCM_8;
  #else
    return 0;
  #endif /* DTLS_PSK */
  }
  
  /** returns true if the application is configured for psk */
  static inline int is_psk_supported(dtls_context_t *ctx)
  {
  #ifdef DTLS_PSK
    return ctx && ctx->h && ctx->h->get_psk_info;
  #else
    return 0;
  #endif /* DTLS_PSK */
  }
  
  /** returns true if the application is configured for ecdhe_ecdsa */
  static inline int is_ecdsa_supported(dtls_context_t *ctx, int is_client)
  {
  #ifdef DTLS_ECC
    return ctx && ctx->h && ((!is_client && ctx->h->get_ecdsa_key) || 
  			   (is_client && ctx->h->verify_ecdsa_key));
  #else
    return 0;
  #endif /* DTLS_ECC */
  }
  
  /** Returns true if the application is configured for ecdhe_ecdsa with
    * client authentication */
  static inline int is_ecdsa_client_auth_supported(dtls_context_t *ctx)
  {
  #ifdef DTLS_ECC
    return ctx && ctx->h && ctx->h->get_ecdsa_key && ctx->h->verify_ecdsa_key;
  #else
    return 0;
  #endif /* DTLS_ECC */
  }
  
  /**
   * 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 && is_tls_psk_with_aes_128_ccm_8(code)) ||
  	 (ecdsa && is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(code));
  }
  
  /** Dump out the cipher keys and IVs used for the symetric cipher. */
  static void dtls_debug_keyblock(dtls_security_parameters_t *config)
  {
    dtls_debug("key_block (%d bytes):\n", dtls_kb_size(config, peer->role));
    dtls_debug_dump("  client_MAC_secret",
  		  dtls_kb_client_mac_secret(config, peer->role),
  		  dtls_kb_mac_secret_size(config, peer->role));
  
    dtls_debug_dump("  server_MAC_secret",
  		  dtls_kb_server_mac_secret(config, peer->role),
  		  dtls_kb_mac_secret_size(config, peer->role));
  
    dtls_debug_dump("  client_write_key",
  		  dtls_kb_client_write_key(config, peer->role),
  		  dtls_kb_key_size(config, peer->role));
  
    dtls_debug_dump("  server_write_key",
  		  dtls_kb_server_write_key(config, peer->role),
  		  dtls_kb_key_size(config, peer->role));
  
    dtls_debug_dump("  client_IV",
  		  dtls_kb_client_iv(config, peer->role),
  		  dtls_kb_iv_size(config, peer->role));
  
    dtls_debug_dump("  server_IV",
  		  dtls_kb_server_iv(config, peer->role),
  		  dtls_kb_iv_size(config, peer->role));
  }
  
  /** returns the name of the goven handshake type number.
    * see IANA for a full list of types:
    * https://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-7
    */
  static char *dtls_handshake_type_to_name(int type)
  {
    switch (type) {
    case DTLS_HT_HELLO_REQUEST:
      return "hello_request";
    case DTLS_HT_CLIENT_HELLO:
      return "client_hello";
    case DTLS_HT_SERVER_HELLO:
      return "server_hello";
    case DTLS_HT_HELLO_VERIFY_REQUEST:
      return "hello_verify_request";
    case DTLS_HT_CERTIFICATE:
      return "certificate";
    case DTLS_HT_SERVER_KEY_EXCHANGE:
      return "server_key_exchange";
    case DTLS_HT_CERTIFICATE_REQUEST:
      return "certificate_request";
    case DTLS_HT_SERVER_HELLO_DONE:
      return "server_hello_done";
    case DTLS_HT_CERTIFICATE_VERIFY:
      return "certificate_verify";
    case DTLS_HT_CLIENT_KEY_EXCHANGE:
      return "client_key_exchange";
    case DTLS_HT_FINISHED:
      return "finished";
    default:
      return "unknown";
    }
  }
  
  /**
   * Calculate the pre master secret and after that calculate the master-secret.
   */
  static int
  calculate_key_block(dtls_context_t *ctx, 
  		    dtls_handshake_parameters_t *handshake,
  		    dtls_peer_t *peer,
  		    session_t *session,
  		    dtls_peer_type role) {
    unsigned char *pre_master_secret;
    int pre_master_len = 0;
    dtls_security_parameters_t *security = dtls_security_params_next(peer);
    uint8 master_secret[DTLS_MASTER_SECRET_LENGTH];
  
    if (!security) {
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    pre_master_secret = security->key_block;
  
    switch (handshake->cipher) {
  #ifdef DTLS_PSK
    case TLS_PSK_WITH_AES_128_CCM_8: {
      unsigned char psk[DTLS_PSK_MAX_KEY_LEN];
      int len;
  
      len = CALL(ctx, get_psk_info, session, DTLS_PSK_KEY,
  	       handshake->keyx.psk.identity,
  	       handshake->keyx.psk.id_length,
  	       psk, DTLS_PSK_MAX_KEY_LEN);
      if (len < 0) {
        dtls_crit("no psk key for session available\n");
        return len;
      }
    /* Temporarily use the key_block storage space for the pre master secret. */
      pre_master_len = dtls_psk_pre_master_secret(psk, len,
  						pre_master_secret,
  						MAX_KEYBLOCK_LENGTH);
  
      dtls_debug_hexdump("psk", psk, len);
  
      memset(psk, 0, DTLS_PSK_MAX_KEY_LEN);
      if (pre_master_len < 0) {
        dtls_crit("the psk was too long, for the pre master secret\n");
        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
      }
  
      break;
    }
  #endif /* DTLS_PSK */
  #ifdef DTLS_ECC
    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,
  						 MAX_KEYBLOCK_LENGTH);
      if (pre_master_len < 0) {
        dtls_crit("the curve was too long, for the pre master secret\n");
        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
      }
      break;
    }
  #endif /* DTLS_ECC */
    default:
      dtls_crit("calculate_key_block: unknown cipher\n");
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    dtls_debug_dump("client_random", handshake->tmp.random.client, DTLS_RANDOM_LENGTH);
    dtls_debug_dump("server_random", handshake->tmp.random.server, DTLS_RANDOM_LENGTH);
    dtls_debug_dump("pre_master_secret", pre_master_secret, pre_master_len);
  
    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_debug_dump("master_secret", master_secret, DTLS_MASTER_SECRET_LENGTH);
  
    /* 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, role));
  
    memcpy(handshake->tmp.master_secret, master_secret, DTLS_MASTER_SECRET_LENGTH);
    dtls_debug_keyblock(security);
  
    security->cipher = handshake->cipher;
    security->compression = handshake->compression;
    security->rseq = 0;
  
    return 0;
  }
  
  /* 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) {
      dtls_warn("the list of the supported elliptic curves should be tls extension length - 2\n");
      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;
    }
  
    dtls_warn("no supported elliptic curve found\n");
    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) {
      dtls_warn("the list of the supported certificate types should be tls extension length - 1\n");
      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_RAW_PUBLIC_KEY)
        return 0;
    }
  
    dtls_warn("no supported certificate type found\n");
    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) {
      dtls_warn("the list of the supported ec_point_formats should be tls extension length - 1\n");
      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;
    }
  
    dtls_warn("no supported ec_point_format found\n");
    return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
  }
  
  /*
   * Check for some TLS Extensions used by the ECDHE_ECDSA cipher.
   */
  static int
  dtls_check_tls_extension(dtls_peer_t *peer,
  			 uint8 *data, size_t data_length, int client_hello)
  {
    uint16_t i, j;
    int ext_elliptic_curve = 0;
    /* no need.*/
    int ext_client_cert_type = 1;
    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 (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) {
        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_CERTIFICATE_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_RAW_PUBLIC_KEY)
  	    goto error;
          }
          break;
        case TLS_EXT_SERVER_CERTIFICATE_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_RAW_PUBLIC_KEY)
  	    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;
        case TLS_EXT_ENCRYPT_THEN_MAC:
  	/* As only AEAD cipher suites are currently available, this
  	 * extension can be skipped. 
  	 */
  	dtls_info("skipped encrypt-then-mac extension\n");
  	break;
        default:
          dtls_warn("unsupported tls extension: %i\n", i);
          break;
      }
      data += j;
      data_length -= j;
    }
    if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && client_hello) {
      if (!ext_elliptic_curve || !ext_client_cert_type || !ext_server_cert_type
  	|| !ext_ec_point_formats) {
        dtls_warn("not all required tls extensions found in client hello\n");
        goto error;
      }
    } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher) && !client_hello) {
      if (!ext_client_cert_type || !ext_server_cert_type) {
        dtls_warn("not all required tls extensions found in server hello\n");
        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);
    }
  }
  
  /**
   * Parses the ClientHello from the client and updates the internal handshake
   * parameters with the new data for the given \p peer. When the ClientHello
   * handshake message in \p data does not contain a cipher suite or
   * compression method, it is copied from the the current security parameters.
   *
   * \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 -Something if an error occurred, \c 0 on success.
   */
  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 = dtls_security_params(peer);
  
    wilddog_assert(config, -1);
    wilddog_assert(data_length > DTLS_HS_LENGTH + DTLS_CH_LENGTH, -1);
  
    /* 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 */
    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 || 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;
      dtls_warn("No matching cipher found\n");
      goto error;
    }
  
    if (data_length < sizeof(uint8)) { 
      /* no compression specified, take the current compression method */
      if (security)
        config->compression = security->compression;
      else
        config->compression = TLS_COMPRESSION_NULL;
      return 0;
    }
  
    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:
    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);
    }
  }
  
  /**
   * Parse the ClientKeyExchange and update the internal handshake state with
   * the new data.
   */
  static inline int
  check_client_keyexchange(dtls_context_t *ctx, 
  			 dtls_handshake_parameters_t *handshake,
  			 uint8 *data, size_t length) {
  
  #ifdef DTLS_ECC
    if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher)) {
  
      if (length < DTLS_HS_LENGTH + DTLS_CKXEC_LENGTH) {
        dtls_debug("The client key exchange is too short\n");
        return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
      }
      data += DTLS_HS_LENGTH;
  
      if (dtls_uint8_to_int(data) != 1 + 2 * DTLS_EC_KEY_SIZE) {
        dtls_alert("expected 65 bytes long public point\n");
        return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
      }
      data += sizeof(uint8);
  
      if (dtls_uint8_to_int(data) != 4) {
        dtls_alert("expected uncompressed public point\n");
        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);
    }
  #endif /* DTLS_ECC */
  #ifdef DTLS_PSK
    if (is_tls_psk_with_aes_128_ccm_8(handshake->cipher)) {
      int id_length;
  
      if (length < DTLS_HS_LENGTH + DTLS_CKXPSK_LENGTH_MIN) {
        dtls_debug("The client key exchange is too short\n");
        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) {
        dtls_debug("The identity has a wrong length\n");
        return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
      }
  
      if (id_length > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
        dtls_warn("please use a smaller client identity\n");
        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
      }
  
      handshake->keyx.psk.id_length = id_length;
      memcpy(handshake->keyx.psk.identity, data, id_length);
    }
  #endif /* DTLS_PSK */
    return 0;
  }
  
  static inline void
  update_hs_hash(dtls_peer_t *peer, uint8 *data, size_t length) {
    dtls_debug_dump("add MAC data", data, length);
    dtls_hash_update(&peer->handshake_params->hs_state.hs_hash, data, length);
  }
  
  static void
  copy_hs_hash(dtls_peer_t *peer, dtls_hash_ctx *hs_hash) {
    memcpy(hs_hash, &peer->handshake_params->hs_state.hs_hash,
  	 sizeof(peer->handshake_params->hs_state.hs_hash));
  }
  
  static inline size_t
  finalize_hs_hash(dtls_peer_t *peer, uint8 *buf) {
    return dtls_hash_finalize(buf, &peer->handshake_params->hs_state.hs_hash);
  }
  
  static inline int
  clear_hs_hash(dtls_peer_t *peer) {
    wilddog_assert(peer, -1);
    dtls_debug("clear MAC\n");
    dtls_hash_init(&peer->handshake_params->hs_state.hs_hash);
    return 0;
  }
  
  /** 
   * 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 data   The cleartext payload of the message.
   * \param data_length Actual length of \p data.
   * \return \c 0 if the Finished message is valid, \c negative number otherwise.
   */
  static int
  check_finished(dtls_context_t *ctx, dtls_peer_t *peer,
  	       uint8 *data, size_t data_length) {
    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->handshake_params->hs_state.hs_hash, DTLS_HASH_CTX_SIZE);
  
    digest_length = finalize_hs_hash(peer, buf);
    /* clear_hash(); */
  
    /* restore hash status */
    memcpy(&peer->handshake_params->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 { /* server */
      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_debug_dump("d:", data + DTLS_HS_LENGTH, sizeof(b.verify_data));
    dtls_debug_dump("v:", b.verify_data, sizeof(b.verify_data));
  
    /* compare verify data and create DTLS alert code when they differ */
    return equals(data + DTLS_HS_LENGTH, b.verify_data, sizeof(b.verify_data))
      ? 0
      : dtls_alert_create(DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_HANDSHAKE_FAILURE);
  }
  
  /**
   * 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 security  The encryption paramater used to encrypt
   * \param type    The content type of this record.
   * \param data_array Array with payloads in correct order.
   * \param data_len_array sizes of the payloads in correct order.
   * \param data_array_len The number of payloads given.
   * \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, dtls_security_parameters_t *security,
  		    unsigned char type,
  		    uint8 *data_array[], size_t data_len_array[],
  		    size_t data_array_len,
  		    uint8 *sendbuf, size_t *rlen) {
    uint8 *p, *start;
    int res;
    unsigned int i;
    
    if (*rlen < DTLS_RH_LENGTH) {
      dtls_alert("The sendbuf (%d bytes) is too small\n", *rlen);
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    p = dtls_set_record_header(type, security, sendbuf);
    start = p;
  
    if (!security || 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 < res + DTLS_RH_LENGTH + data_len_array[i]) {
          dtls_debug("dtls_prepare_record: send buffer too small\n");
          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 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */   
      /** 
       * 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 (is_tls_psk_with_aes_128_ccm_8(security->cipher)) {
        dtls_debug("dtls_prepare_record(): encrypt using TLS_PSK_WITH_AES_128_CCM_8\n");
      } else if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(security->cipher)) {
        dtls_debug("dtls_prepare_record(): encrypt using TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8\n");
      } else {
        dtls_debug("dtls_prepare_record(): encrypt using unknown cipher\n");
      }
  
      /* set nonce       
         from RFC 6655:
     	The "nonce" input to the AEAD algorithm is exactly that of [RFC5288]:
     	the "nonce" SHALL be 12 bytes long and is constructed as follows:
     	(this is an example of a "partially explicit" nonce; see Section
     	3.2.1 in [RFC5116]).
  
                         struct {
               opaque salt[4];
               opaque nonce_explicit[8];
                         } CCMNonce;
  
           [...]
  
    	 In DTLS, the 64-bit seq_num is the 16-bit epoch concatenated with the
     	 48-bit seq_num.
  
     	 When the nonce_explicit is equal to the sequence number, the CCMNonce
     	 will have the structure of the CCMNonceExample given below.
  
     	            struct {
     	             uint32 client_write_IV; // low order 32-bits
     	             uint64 seq_num;         // TLS sequence number
     	            } CCMClientNonce.
  
  
     	            struct {
     	             uint32 server_write_IV; // low order 32-bits
     	             uint64 seq_num; // TLS sequence number
     	            } CCMServerNonce.
  
  
     	            struct {
     	             case client:
     	               CCMClientNonce;
     	             case server:
     	               CCMServerNonce:
     	            } CCMNonceExample;
      */
  
      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 + DTLS_RH_LENGTH + data_len_array[i]) {
          dtls_debug("dtls_prepare_record: send buffer too small\n");
          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 */
  
      dtls_debug_dump("nonce:", nonce, DTLS_CCM_BLOCKSIZE);
      dtls_debug_dump("key:", dtls_kb_local_write_key(security, peer->role),
  		    dtls_kb_key_size(security, peer->role));
      
      /* 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(start + 8, res - 8, start + 8, nonce,
  		       dtls_kb_local_write_key(security, peer->role),
  		       dtls_kb_key_size(security, peer->role),
  		       A_DATA, A_DATA_LEN);
  
      if (res < 0)
        return res;
  
      res += 8;			/* increment res by size of nonce_explicit */
      dtls_debug_dump("message:", start, res);
    }
  
    /* 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)
  {
    uint8 buf[DTLS_HS_LENGTH];
    uint8 *data_array[2];
    size_t data_len_array[2];
    int i = 0;
    dtls_security_parameters_t *security = peer ? dtls_security_params(peer) : NULL;
  
    dtls_set_handshake_header(header_type, peer, 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++;
    }
    dtls_debug("send handshake packet of type: %s (%i)\n",
  	     dtls_handshake_type_to_name(header_type), header_type);
    return dtls_send_multi(ctx, peer, security, session, DTLS_CT_HANDSHAKE,
  			 data_array, data_len_array, i);
  }
  
  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_multi(dtls_context_t *ctx, dtls_peer_t *peer,
  		dtls_security_parameters_t *security , session_t *session,
  		unsigned char type, uint8 *buf_array[],
  		size_t buf_len_array[], size_t buf_array_len)
  {
    /* 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;
    unsigned int i;
    size_t overall_len = 0;
  
    res = dtls_prepare_record(peer, security, type, buf_array, buf_len_array, buf_array_len, sendbuf, &len);
  
    if (res < 0){
      wilddog_debug_level(WD_DEBUG_ERROR,"Prepare record failed");
      return res;
    }
    /* if (peer && MUST_HASH(peer, type, buf, buflen)) */
    /*   update_hs_hash(peer, buf, buflen); */
  
    dtls_debug_hexdump("send header", sendbuf, sizeof(dtls_record_header_t));
    for (i = 0; i < buf_array_len; i++) {
      dtls_debug_hexdump("send unencrypted", buf_array[i], buf_len_array[i]);
      overall_len += buf_len_array[i];
    }
  
    if ((type == DTLS_CT_HANDSHAKE && buf_array[0][0] != DTLS_HT_HELLO_VERIFY_REQUEST) ||
        type == DTLS_CT_CHANGE_CIPHER_SPEC) {
      /* copy handshake messages other than HelloVerify into retransmit buffer */
      netq_t *n = netq_node_new(overall_len);
      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->epoch = (security) ? security->epoch : 0;
        n->type = type;
        n->length = 0;
        for (i = 0; i < buf_array_len; i++) {
          memcpy(n->data + n->length, buf_array[i], buf_len_array[i]);
          n->length += buf_len_array[i];
        }
  
        if (!netq_insert_node(ctx->sendqueue, n)) {
  	dtls_warn("cannot add packet to retransmit buffer\n");
  	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 */
  	dtls_debug("copied to sendqueue\n");
  #endif /* WITH_CONTIKI */
        }
      } else 
        dtls_warn("retransmit buffer full\n");
    }
  
    /* FIXME: copy to peer's sendqueue (after fragmentation if
     * necessary) and initialize retransmit timer */
    res = CALL(ctx, write, 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 : overall_len - (len - res);
  }
  
  static inline int
  dtls_send_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_send_alert(ctx, peer, DTLS_ALERT_LEVEL_FATAL, DTLS_ALERT_CLOSE_NOTIFY);
      /* indicate tear down */
      peer->state = DTLS_STATE_CLOSING;
    }
    return res;
  }
  
  static void dtls_destroy_peer(dtls_context_t *ctx, dtls_peer_t *peer, int unlink)
  {
    if (peer->state != DTLS_STATE_CLOSED && peer->state != DTLS_STATE_CLOSING)
      dtls_close(ctx, &peer->session);
    if (unlink) {
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
      HASH_DEL_PEER(ctx->peers, peer);
  #else /* WITH_CONTIKI */
      list_remove((void **)&ctx->peers, peer);
  #endif /* WITH_CONTIKI */
      dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "removed peer", &peer->session);
    }
    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 *data, size_t data_length)
  {
    uint8 buf[DTLS_HV_LENGTH + DTLS_COOKIE_LENGTH];
    uint8 *p = buf;
    int len = DTLS_COOKIE_LENGTH;
    uint8 *cookie = NULL;
    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_debug_dump("create cookie", mycookie, len);
  
    wilddog_assert(len == DTLS_COOKIE_LENGTH, -1);
      
    /* Perform cookie check. */
    len = dtls_get_cookie(data, data_length, &cookie);
    if (len < 0) {
      dtls_warn("error while fetching the cookie, err: %i\n", err);
      return err;
    }
  
    dtls_debug_dump("compare with cookie", cookie, len);
  
    /* check if cookies match */
    if (len == DTLS_COOKIE_LENGTH && memcmp(cookie, mycookie, len) == 0) {
      dtls_debug("found matching cookie\n");
      return 0;
    }
  
    if (len > 0) {
      dtls_debug_dump("invalid cookie", cookie, len);
    } else {
      dtls_debug("cookie len is 0!\n");
    }
  
    /* 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);
  
    wilddog_assert(p == mycookie, -1);
  
    p += DTLS_COOKIE_LENGTH;
  
    /* TODO use the same record sequence number as in the ClientHello,
       see 4.2.1. Denial-of-Service Countermeasures */
    err = dtls_send_handshake_msg_hash(ctx, peer, session,
  				     DTLS_HT_HELLO_VERIFY_REQUEST,
  				     buf, p - buf, 0);
    if (err < 0) {
      dtls_warn("cannot send HelloVerify request\n");
    }
    return err; /* HelloVerify is sent, now we cannot do anything but wait */
  
  #undef mycookie
  }
  
  #ifdef DTLS_ECC
  static int
  dtls_check_ecdsa_signature_elem(uint8 *data, size_t data_length,
  				unsigned char **result_r,
  				unsigned char **result_s)
  {
    int i;
    uint8 *data_orig = data;
  
    if (dtls_uint8_to_int(data) != TLS_EXT_SIG_HASH_ALGO_SHA256) {
      dtls_alert("only sha256 is supported in certificate verify\n");
      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) {
      dtls_alert("only ecdsa signature is supported in client verify\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    data += sizeof(uint8);
    data_length -= sizeof(uint8);
  
    if (data_length < dtls_uint16_to_int(data)) {
      dtls_alert("signature length wrong\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    data += sizeof(uint16);
    data_length -= sizeof(uint16);
  
    if (dtls_uint8_to_int(data) != 0x30) {
      dtls_alert("wrong ASN.1 struct, expected SEQUENCE\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    data += sizeof(uint8);
    data_length -= sizeof(uint8);
  
    if (data_length < dtls_uint8_to_int(data)) {
      dtls_alert("signature length wrong\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    data += sizeof(uint8);
    data_length -= sizeof(uint8);
  
    if (dtls_uint8_to_int(data) != 0x02) {
      dtls_alert("wrong ASN.1 struct, expected Integer\n");
      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) {
      dtls_alert("wrong ASN.1 struct, expected Integer\n");
      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;
  
    return data - data_orig;
  }
  
  static int
  check_client_certificate_verify(dtls_context_t *ctx, 
  				dtls_peer_t *peer,
  				uint8 *data, size_t data_length)
  {
    dtls_handshake_parameters_t *config = peer->handshake_params;
    int ret;
    unsigned char *result_r;
    unsigned char *result_s;
    dtls_hash_ctx hs_hash;
    unsigned char sha256hash[DTLS_HMAC_DIGEST_SIZE];
  
    wilddog_assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher), -1);
  
    data += DTLS_HS_LENGTH;
  
    if (data_length < DTLS_HS_LENGTH + DTLS_CV_LENGTH) {
      dtls_alert("the packet length does not match the expected\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
  
    ret = dtls_check_ecdsa_signature_elem(data, data_length, &result_r, &result_s);
    if (ret < 0) {
      return ret;
    }
    data += ret;
    data_length -= ret;
  
    copy_hs_hash(peer, &hs_hash);
  
    dtls_hash_finalize(sha256hash, &hs_hash);
  
    ret = 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 (ret < 0) {
      dtls_alert("wrong signature err: %i\n", ret);
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    return 0;
  }
  #endif /* DTLS_ECC */
  
  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 = is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(handshake->cipher);
  
    extension_size = (ecdsa) ? 2 + 5 + 5 + 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);
    dtls_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 */
      *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_CERTIFICATE_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_RAW_PUBLIC_KEY);
      p += sizeof(uint8);
  
      /* client certificate type extension */
      dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_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_RAW_PUBLIC_KEY);
      p += sizeof(uint8);
  
      /* 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);
    }
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    /* TODO use the same record sequence number as in the ClientHello,
       see 4.2.1. Denial-of-Service Countermeasures */
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_HELLO,
  				 buf, p - buf);
  }
  
  #ifdef DTLS_ECC
  static int
  dtls_send_certificate_ecdsa(dtls_context_t *ctx, dtls_peer_t *peer,
  			    const dtls_ecdsa_key_t *key)
  {
    uint8 buf[DTLS_CE_LENGTH];
    uint8 *p;
  
    /* Certificate 
     *
     * Start message construction at beginning of buffer. */
    p = buf;
  
    dtls_int_to_uint24(p, 94);	/* certificates length */
    p += sizeof(uint24);
  
    dtls_int_to_uint24(p, 91);	/* length of this certificate */
    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;
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    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 (1 + 1 + 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
  
    /* 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);
  
    /* 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, TLS_EXT_ELLIPTIC_CURVES_SECP256R1);
    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 spec. */
    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);
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
  				 buf, p - buf);
  }
  #endif /* DTLS_ECC */
  
  #ifdef DTLS_PSK
  static int
  dtls_send_server_key_exchange_psk(dtls_context_t *ctx, dtls_peer_t *peer,
  				  const unsigned char *psk_hint, size_t len)
  {
    uint8 buf[DTLS_SKEXECPSK_LENGTH_MAX];
    uint8 *p;
  
    p = buf;
  
    wilddog_assert(len <= DTLS_PSK_MAX_CLIENT_IDENTITY_LEN, -1);
    if (len > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
      /* should never happen */
      dtls_warn("psk identity hint is too long\n");
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    dtls_int_to_uint16(p, len);
    p += sizeof(uint16);
  
    memcpy(p, psk_hint, len);
    p += len;
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_SERVER_KEY_EXCHANGE,
  				 buf, p - buf);
  }
  #endif /* DTLS_PSK */
  
  #ifdef DTLS_ECC
  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, TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN);
    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);
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_REQUEST,
  				 buf, p - buf);
  }
  #endif /* DTLS_ECC */
  
  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) {
      dtls_debug("dtls_server_hello: cannot prepare ServerHello record\n");
      return res;
    }
  
  #ifdef DTLS_ECC
    if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
      const dtls_ecdsa_key_t *ecdsa_key;
  
      res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
      if (res < 0) {
        dtls_crit("no ecdsa certificate to send in certificate\n");
        return res;
      }
  
      res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
  
      if (res < 0) {
        dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
        return res;
      }
  
      res = dtls_send_server_key_exchange_ecdh(ctx, peer, ecdsa_key);
  
      if (res < 0) {
        dtls_debug("dtls_server_hello: cannot prepare Server Key Exchange record\n");
        return res;
      }
  
      if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
  	is_ecdsa_client_auth_supported(ctx)) {
        res = dtls_send_server_certificate_request(ctx, peer);
  
        if (res < 0) {
          dtls_debug("dtls_server_hello: cannot prepare certificate Request record\n");
          return res;
        }
      }
    }
  #endif /* DTLS_ECC */
  
  #ifdef DTLS_PSK
    if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
      unsigned char psk_hint[DTLS_PSK_MAX_CLIENT_IDENTITY_LEN];
      int len;
  
      /* The identity hint is optional, therefore we ignore the result
       * and check psk only. */
      len = CALL(ctx, get_psk_info, &peer->session, DTLS_PSK_HINT,
  	       NULL, 0, psk_hint, DTLS_PSK_MAX_CLIENT_IDENTITY_LEN);
  
      if (len < 0) {
        dtls_debug("dtls_server_hello: cannot create ServerKeyExchange\n");
        return len;
      }
  
      if (len > 0) {
        res = dtls_send_server_key_exchange_psk(ctx, peer, psk_hint, (size_t)len);
  
        if (res < 0) {
  	dtls_debug("dtls_server_key_exchange_psk: cannot send server key exchange record\n");
  	return res;
        }
      }
    }
  #endif /* DTLS_PSK */
  
    res = dtls_send_server_hello_done(ctx, peer);
  
    if (res < 0) {
      dtls_debug("dtls_server_hello: cannot prepare ServerHelloDone record\n");
      return res;
    }
    return 0;
  }
  
  static inline int 
  dtls_send_ccs(dtls_context_t *ctx, dtls_peer_t *peer) {
    uint8 buf[1] = {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;
    dtls_handshake_parameters_t *handshake = peer->handshake_params;
  
    p = buf;
  
    switch (handshake->cipher) {
  #ifdef DTLS_PSK
    case TLS_PSK_WITH_AES_128_CCM_8: {
      int len;
  
      len = CALL(ctx, get_psk_info, &peer->session, DTLS_PSK_IDENTITY,
  	       handshake->keyx.psk.identity, handshake->keyx.psk.id_length,
  	       buf + sizeof(uint16),
  	       min(sizeof(buf) - sizeof(uint16),
  		   sizeof(handshake->keyx.psk.identity)));
      if (len < 0) {
        dtls_crit("no psk identity set in kx\n");
        return len;
      }
  
      if (len + sizeof(uint16) > DTLS_CKXEC_LENGTH) {
        memset(&handshake->keyx.psk, 0, sizeof(dtls_handshake_parameters_psk_t));
        dtls_warn("the psk identity is too long\n");
        return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
      }
      handshake->keyx.psk.id_length = (unsigned int)len;
      memcpy(handshake->keyx.psk.identity, p + sizeof(uint16), len);
  
      dtls_int_to_uint16(p, handshake->keyx.psk.id_length);
      p += sizeof(uint16);
  
      memcpy(p, handshake->keyx.psk.identity, handshake->keyx.psk.id_length);
      p += handshake->keyx.psk.id_length;
  
      break;
    }
  #endif /* DTLS_PSK */
  #ifdef DTLS_ECC
    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 spec. */
      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;
    }
  #endif /* DTLS_ECC */
    default:
      dtls_crit("cipher not supported\n");
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CLIENT_KEY_EXCHANGE,
  				 buf, p - buf);
  }
  
  #ifdef DTLS_ECC
  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;
  
    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);
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    return dtls_send_handshake_msg(ctx, peer, DTLS_HT_CERTIFICATE_VERIFY,
  				 buf, p - buf);
  }
  #endif /* DTLS_ECC */
  
  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_debug_dump("server finished MAC", p, DTLS_FIN_LENGTH);
  
    p += DTLS_FIN_LENGTH;
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    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) {
    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) {
      dtls_crit("no cipher callbacks implemented\n");
    }
  
    dtls_int_to_uint16(p, DTLS_VERSION);
    p += sizeof(uint16);
  
    if (cookie_length > DTLS_COOKIE_LENGTH_MAX) {
      dtls_warn("the cookie is too long\n");
      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);
      dtls_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_CERTIFICATE_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_RAW_PUBLIC_KEY);
      p += sizeof(uint8);
  
      /* client certificate type extension */
      dtls_int_to_uint16(p, TLS_EXT_SERVER_CERTIFICATE_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_RAW_PUBLIC_KEY);
      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);
    }
  
    wilddog_assert(p - buf <= sizeof(buf), -1);
  
    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) {
      dtls_alert("unknown DTLS version\n");
      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)) {
      dtls_alert("unsupported cipher 0x%02x 0x%02x\n",
  	     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) {
      dtls_alert("unsupported compression method 0x%02x\n", 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)
  {
    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)
      dtls_warn("cannot send ClientHello\n");
  
    return res;
  }
  
  #ifdef DTLS_ECC
  static int
  check_server_certificate(dtls_context_t *ctx, 
  			 dtls_peer_t *peer,
  			 uint8 *data, size_t data_length)
  {
    int err;
    dtls_handshake_parameters_t *config = peer->handshake_params;
  
    update_hs_hash(peer, data, data_length);
  
    wilddog_assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher), -1);
  
    data += DTLS_HS_LENGTH;
    
    if (dtls_uint24_to_int(data) == 94) {    
      /*skip certificate message length.*/
      data += sizeof(uint24);
  #if 0
      dtls_alert("expect length of 94 bytes for server certificate message\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
   #endif   
    }
    
    if (dtls_uint24_to_int(data) != 91) {
      dtls_alert("expect length of 91 bytes for certificate\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    data += sizeof(uint24);
  
    if (memcmp(data, cert_asn1_header, sizeof(cert_asn1_header))) {
      dtls_alert("got an unexpected Subject public key format\n");
      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) {
      dtls_warn("The certificate was not accepted\n");
      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)
  {
    dtls_handshake_parameters_t *config = peer->handshake_params;
    int ret;
    unsigned char *result_r;
    unsigned char *result_s;
    unsigned char *key_params;
  
    update_hs_hash(peer, data, data_length);
  
    wilddog_assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(config->cipher), -1);
  
    data += DTLS_HS_LENGTH;
  
    if (data_length < DTLS_HS_LENGTH + DTLS_SKEXEC_LENGTH) {
      dtls_alert("the packet length does not match the expected\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    key_params = data;
  
    if (dtls_uint8_to_int(data) != TLS_EC_CURVE_TYPE_NAMED_CURVE) {
      dtls_alert("Only named curves supported\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    data += sizeof(uint8);
    data_length -= sizeof(uint8);
  
    if (dtls_uint16_to_int(data) != TLS_EXT_ELLIPTIC_CURVES_SECP256R1) {
      dtls_alert("secp256r1 supported\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    data += sizeof(uint16);
    data_length -= sizeof(uint16);
  
    if (dtls_uint8_to_int(data) != 1 + 2 * DTLS_EC_KEY_SIZE) {
      dtls_alert("expected 65 bytes long public point\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    data += sizeof(uint8);
    data_length -= sizeof(uint8);
  
    if (dtls_uint8_to_int(data) != 4) {
      dtls_alert("expected uncompressed public point\n");
      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);
  
    ret = dtls_check_ecdsa_signature_elem(data, data_length, &result_r, &result_s);
    if (ret < 0) {
      return ret;
    }
    data += ret;
    data_length -= ret;
  
    ret = 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 (ret < 0) {
      dtls_alert("wrong signature\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
    return 0;
  }
  #endif /* DTLS_ECC */
  
  #ifdef DTLS_PSK
  static int
  check_server_key_exchange_psk(dtls_context_t *ctx,
  			      dtls_peer_t *peer,
  			      uint8 *data, size_t data_length)
  {
    dtls_handshake_parameters_t *config = peer->handshake_params;
    uint16_t len;
  
    update_hs_hash(peer, data, data_length);
  
    wilddog_assert(is_tls_psk_with_aes_128_ccm_8(config->cipher), -1);
  
    data += DTLS_HS_LENGTH;
  
    if (data_length < DTLS_HS_LENGTH + DTLS_SKEXECPSK_LENGTH_MIN) {
      dtls_alert("the packet length does not match the expected\n");
      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)) {
      dtls_warn("the length of the server identity hint is worng\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
  
    if (len > DTLS_PSK_MAX_CLIENT_IDENTITY_LEN) {
      dtls_warn("please use a smaller server identity hint\n");
      return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
    }
  
    /* store the psk_identity_hint in config->keyx.psk for later use */
    config->keyx.psk.id_length = len;
    memcpy(config->keyx.psk.identity, data, len);
    return 0;
  }
  #endif /* DTLS_PSK */
  
  static int
  check_certificate_request(dtls_context_t *ctx, 
  			  dtls_peer_t *peer,
  			  uint8 *data, size_t data_length)
  {
    unsigned int i;
    int auth_alg;
    int sig_alg;
    int hash_alg;
  
    update_hs_hash(peer, data, data_length);
  
    wilddog_assert(is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher), -1);
  
    data += DTLS_HS_LENGTH;
  
    if (data_length < DTLS_HS_LENGTH + 5) {
      dtls_alert("the packet length does not match the expected\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
  
    i = dtls_uint8_to_int(data);
    data += sizeof(uint8);
    if (i + 1 > data_length) {
      dtls_alert("the cerfificate types are too long\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
  
    auth_alg = 0;
    for (; i > 0 ; i -= sizeof(uint8)) {
      if (dtls_uint8_to_int(data) == TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN
  	&& auth_alg == 0)
        auth_alg = dtls_uint8_to_int(data);
      data += sizeof(uint8);
    }
  
    if (auth_alg != TLS_CLIENT_CERTIFICATE_TYPE_ECDSA_SIGN) {
      dtls_alert("the request authentication algorithm is not supproted\n");
      return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
    }
  
    i = dtls_uint16_to_int(data);
    data += sizeof(uint16);
    if (i + 1 > data_length) {
      dtls_alert("the signature and hash algorithm list is too long\n");
      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 == TLS_EXT_SIG_HASH_ALGO_SHA256 && hash_alg == 0 && 
          current_sig_alg == TLS_EXT_SIG_HASH_ALGO_ECDSA && sig_alg == 0) {
        hash_alg = current_hash_alg;
        sig_alg = current_sig_alg;
      }
    }
  
    if (hash_alg != TLS_EXT_SIG_HASH_ALGO_SHA256 ||
        sig_alg != TLS_EXT_SIG_HASH_ALGO_ECDSA) {
      dtls_alert("no supported hash and signature algorithem\n");
      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;
  #ifdef DTLS_ECC
    const dtls_ecdsa_key_t *ecdsa_key;
  #endif /* DTLS_ECC */
  
    dtls_handshake_parameters_t *handshake = peer->handshake_params;
  
    /* calculate master key, send CCS */
  
    update_hs_hash(peer, data, data_length);
  
  #ifdef DTLS_ECC
    if (handshake->do_client_auth) {
  
      res = CALL(ctx, get_ecdsa_key, &peer->session, &ecdsa_key);
      if (res < 0) {
        dtls_crit("no ecdsa certificate to send in certificate\n");
        return res;
      }
  
      res = dtls_send_certificate_ecdsa(ctx, peer, ecdsa_key);
  
      if (res < 0) {
        dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
        return res;
      }
    }
  #endif /* DTLS_ECC */
  
    /* send ClientKeyExchange */
    res = dtls_send_client_key_exchange(ctx, peer);
  
    if (res < 0) {
      dtls_debug("cannot send KeyExchange message\n");
      return res;
    }
  
  #ifdef DTLS_ECC
    if (handshake->do_client_auth) {
  
      res = dtls_send_certificate_verify_ecdh(ctx, peer, ecdsa_key);
  
      if (res < 0) {
        dtls_debug("dtls_server_hello: cannot prepare Certificate record\n");
        return res;
      }
    }
  #endif /* DTLS_ECC */
  
    res = calculate_key_block(ctx, handshake, peer,
  			    &peer->session, peer->role);
    if (res < 0) {
      return res;
    }
  
    res = dtls_send_ccs(ctx, peer);
    if (res < 0) {
      dtls_debug("cannot send CCS message\n");
      return res;
    }
  
    /* and switch cipher suite */
    dtls_security_params_switch(peer);
  
    /* Client 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)
  {
    dtls_record_header_t *header = DTLS_RECORD_HEADER(packet);
    dtls_security_parameters_t *security = dtls_security_params_epoch(peer, dtls_get_epoch(header));
    int clen;
    
    *cleartext = (uint8 *)packet + sizeof(dtls_record_header_t);
    clen = length - sizeof(dtls_record_header_t);
  
    if (!security) {
      dtls_alert("No security context for epoch: %i\n", dtls_get_epoch(header));
      return -1;
    }
  
    if (security->cipher == TLS_NULL_WITH_NULL_NULL) {
      /* no cipher suite selected */
      return clen;
    } else { /* TLS_PSK_WITH_AES_128_CCM_8 or TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 */
      /** 
       * 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 (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;
  
      dtls_debug_dump("nonce", nonce, DTLS_CCM_BLOCKSIZE);
      dtls_debug_dump("key", dtls_kb_remote_write_key(security, peer->role),
  		    dtls_kb_key_size(security, peer->role));
      dtls_debug_dump("ciphertext", *cleartext, clen);
  
      /* 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 */
  
      clen = dtls_decrypt(*cleartext, clen, *cleartext, nonce,
  		       dtls_kb_remote_write_key(security, peer->role),
  		       dtls_kb_key_size(security, peer->role),
  		       A_DATA, A_DATA_LEN);
      if (clen < 0)
        dtls_warn("decryption failed\n");
      else {
  
        dtls_security_params_free_other(peer);
        dtls_debug_dump("cleartext", *cleartext, clen);
      }
    }
    return clen;
  }
  
  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;
  
    peer->handshake_params = dtls_handshake_new();
    if (!peer->handshake_params)
      return -1;
    dtls_security_free(peer->security_params[0]);
    dtls_security_free(peer->security_params[1]);
    peer->security_params[0] = dtls_security_new();
    peer->security_params[1] = dtls_security_new();
    peer->handshake_params->hs_state.mseq_r = 0;
    peer->handshake_params->hs_state.mseq_s = 0;
  
    if (peer->role == DTLS_CLIENT) {
      /* send ClientHello with empty Cookie */
      err = dtls_send_client_hello(ctx, peer, NULL, 0);
      if (err < 0)
        dtls_warn("cannot send ClientHello\n");
      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_msg(dtls_context_t *ctx, dtls_peer_t *peer, session_t *session,
  		 const dtls_peer_type role, const dtls_state_t state,
  		 uint8 *data, size_t data_length) {
  
    int err = 0;
  
    /* This will clear the retransmission buffer if we get an expected
     * handshake message. We have to make sure that no handshake message
     * should get expected when we still should retransmit something, when
     * we do everything accordingly to the DTLS 1.2 standard this should
     * not be a problem. */
    if (peer) {
      dtls_stop_retransmission(ctx, peer);
    }
  
    /* 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. */
  
    dtls_debug("handle handshake packet of type: %s (%i)\n",
  	     dtls_handshake_type_to_name(data[0]), data[0]);
    switch (data[0]) {
  
    /************************************************************************
     * Client states
     ************************************************************************/
    case 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) {
        dtls_warn("error in check_server_hello_verify_request err: %i\n", err);
        return err;
      }
  
      break;
    case 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) {
        dtls_warn("error in check_server_hello err: %i\n", err);
        return err;
      }
      if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher))
        peer->state = DTLS_STATE_WAIT_SERVERCERTIFICATE;
      else
        peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
      /* update_hs_hash(peer, data, data_length); */
  
      break;
  
  #ifdef DTLS_ECC
    case DTLS_HT_CERTIFICATE:
  #ifdef WILDDOG_SELFTEST     
  	  performtest_getDtlsHskVerifyTime();
  #endif
      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) {
        dtls_warn("error in check_server_certificate err: %i\n", 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); */
  #ifdef WILDDOG_SELFTEST     
  		  performtest_getDtlsHskVerifyTime();
  #endif
  
      break;
  #endif /* DTLS_ECC */
  
    case DTLS_HT_SERVER_KEY_EXCHANGE:
  
  #ifdef DTLS_ECC
      if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
        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);
      }
  #endif /* DTLS_ECC */
  #ifdef DTLS_PSK
      if (is_tls_psk_with_aes_128_ccm_8(peer->handshake_params->cipher)) {
        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);
      }
  #endif /* DTLS_PSK */
  
      if (err < 0) {
        dtls_warn("error in check_server_key_exchange err: %i\n", err);
        return err;
      }
      peer->state = DTLS_STATE_WAIT_SERVERHELLODONE;
      /* update_hs_hash(peer, data, data_length); */
  
      break;
  
    case 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) {
        dtls_warn("error in check_server_hellodone err: %i\n", err);
        return err;
      }
      peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
      /* update_hs_hash(peer, data, data_length); */
  
      break;
  
    case 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) {
        dtls_warn("error in check_certificate_request err: %i\n", err);
        return err;
      }
  
      break;
  
    case DTLS_HT_FINISHED:
      /* expect a Finished message from server */
  
      if (state != DTLS_STATE_WAIT_FINISHED) {
        return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
      }
  
      err = check_finished(ctx, peer, data, data_length);
      if (err < 0) {
        dtls_warn("error in check_finished err: %i\n", err);
        return err;
      }
      if (role == DTLS_SERVER) {
        /* send ServerFinished */
        update_hs_hash(peer, data, data_length);
  
        /* send change cipher spec message and switch to new configuration */
        err = dtls_send_ccs(ctx, peer);
        if (err < 0) {
          dtls_warn("cannot send CCS message\n");
          return err;
        }
  
        dtls_security_params_switch(peer);
  
        err = dtls_send_finished(ctx, peer, PRF_LABEL(server), PRF_LABEL_SIZE(server));
        if (err < 0) {
          dtls_warn("sending server Finished failed\n");
          return err;
        }
      }
      dtls_handshake_free(peer->handshake_params);
      peer->handshake_params = NULL;
      dtls_debug("Handshake complete\n");
      check_stack();
      peer->state = DTLS_STATE_CONNECTED;
  
      /* return here to not increase the message receive counter */
      return err;
  
    /************************************************************************
     * Server states
     ************************************************************************/
  
    case DTLS_HT_CLIENT_KEY_EXCHANGE:
      /* handle ClientHello, update msg and msglen and goto next if not finished */
  
      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) {
        dtls_warn("error in check_client_keyexchange err: %i\n", err);
        return err;
      }
      update_hs_hash(peer, data, data_length);
  
      if (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
  	is_ecdsa_client_auth_supported(ctx))
        peer->state = DTLS_STATE_WAIT_CERTIFICATEVERIFY;
      else
        peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
      break;
  
  #ifdef DTLS_ECC
    case 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) {
        dtls_warn("error in check_client_certificate_verify err: %i\n", err);
        return err;
      }
  
      update_hs_hash(peer, data, data_length);
      peer->state = DTLS_STATE_WAIT_CHANGECIPHERSPEC;
      break;
  #endif /* DTLS_ECC */
  
    case 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, data, data_length);
      if (err < 0) {
        dtls_warn("error in dtls_verify_peer err: %i\n", err);
        return err;
      }
  
      if (err > 0) {
        dtls_debug("server hello verify was sent\n");
        break;
      }
  
      /* At this point, we have a good relationship with this peer. This
       * state is left for re-negotiation of key material. */
      if (!peer) {
        dtls_security_parameters_t *security;
  
        /* 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) {
          dtls_alert("cannot create peer\n");
          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.
         */
        security = dtls_security_params(peer);
        security->rseq = 1;
        dtls_add_peer(ctx, peer);
      }
      if (peer && !peer->handshake_params) {
        dtls_handshake_header_t *hs_header = DTLS_HANDSHAKE_HEADER(data);
  
        peer->handshake_params = dtls_handshake_new();
        if (!peer->handshake_params)
          return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
  
        LIST_STRUCT_INIT(peer->handshake_params, reorder_queue);
        peer->handshake_params->hs_state.mseq_r = dtls_uint16_to_int(hs_header->message_seq);
        peer->handshake_params->hs_state.mseq_s = 1;
      }
  
      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) {
        dtls_warn("error updating security parameters\n");
        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 (is_tls_ecdhe_ecdsa_with_aes_128_ccm_8(peer->handshake_params->cipher) &&
  	is_ecdsa_client_auth_supported(ctx))
        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:
  
      if (state != DTLS_STATE_CONNECTED) {
        /* we should just ignore such packets when in handshake */
        return 0;
      }
  
      if (peer && !peer->handshake_params) {
        peer->handshake_params = dtls_handshake_new();
        if (!peer->handshake_params)
          return dtls_alert_fatal_create(DTLS_ALERT_INTERNAL_ERROR);
  
        LIST_STRUCT_INIT(peer->handshake_params, reorder_queue);
        peer->handshake_params->hs_state.mseq_r = 0;
        peer->handshake_params->hs_state.mseq_s = 0;
      }
  
      /* send ClientHello with empty Cookie */
      err = dtls_send_client_hello(ctx, peer, NULL, 0);
      if (err < 0) {
        dtls_warn("cannot send ClientHello\n");
        return err;
      }
      peer->state = DTLS_STATE_CLIENTHELLO;
      break;
  
    default:
      dtls_crit("unhandled message %d\n", data[0]);
      return dtls_alert_fatal_create(DTLS_ALERT_UNEXPECTED_MESSAGE);
    }
  
    if (peer && peer->handshake_params && err >= 0) {
      peer->handshake_params->hs_state.mseq_r++;
    }
  
    return err;
  }
        
  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 *data, size_t data_length)
  {
    dtls_handshake_header_t *hs_header;
    int res;
  
    if (data_length < DTLS_HS_LENGTH) {
      dtls_warn("handshake message too short\n");
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
    }
    hs_header = DTLS_HANDSHAKE_HEADER(data);
  
    dtls_debug("received handshake packet of type: %s (%i)\n",
  	     dtls_handshake_type_to_name(hs_header->msg_type), hs_header->msg_type);
  
    if (!peer || !peer->handshake_params) {
      /* This is the initial ClientHello */
      if (hs_header->msg_type != DTLS_HT_CLIENT_HELLO && !peer) {
        dtls_warn("If there is no peer only ClientHello is allowed\n");
        return dtls_alert_fatal_create(DTLS_ALERT_HANDSHAKE_FAILURE);
      }
  
      /* This is a ClientHello or Hello Request send when doing TLS renegotiation */
      if (hs_header->msg_type == DTLS_HT_CLIENT_HELLO ||
  	hs_header->msg_type == DTLS_HT_HELLO_REQUEST) {
        return handle_handshake_msg(ctx, peer, session, role, state, data,
  				  data_length);
      } else {
        dtls_warn("ignore unexpected handshake message\n");
        return 0;
      }
    }
  
    if (dtls_uint16_to_int(hs_header->message_seq) < peer->handshake_params->hs_state.mseq_r) {
      dtls_warn("The message sequence number is too small, expected %i, got: %i\n",
  	      peer->handshake_params->hs_state.mseq_r, dtls_uint16_to_int(hs_header->message_seq));
      return 0;
    } else if (dtls_uint16_to_int(hs_header->message_seq) > peer->handshake_params->hs_state.mseq_r) {
      /* A packet in between is missing, buffer this packet. */
      netq_t *n;
  
      /* TODO: only add packet that are not too new. */
      if (data_length > DTLS_MAX_BUF) {
        dtls_warn("the packet is too big to buffer for reoder\n");
        return 0;
      }
  
      netq_t *node = netq_head(peer->handshake_params->reorder_queue);
      while (node) {
        dtls_handshake_header_t *node_header = DTLS_HANDSHAKE_HEADER(node->data);
        if (dtls_uint16_to_int(node_header->message_seq) == dtls_uint16_to_int(hs_header->message_seq)) {
          dtls_warn("a packet with this sequence number is already stored\n");
          return 0;
        }
        node = netq_next(node);
      }
  
      n = netq_node_new(data_length);
      if (!n) {
        dtls_warn("no space in reoder buffer\n");
        return 0;
      }
  
      n->peer = peer;
      n->length = data_length;
      memcpy(n->data, data, data_length);
  
      if (!netq_insert_node(peer->handshake_params->reorder_queue, n)) {
        dtls_warn("cannot add packet to reoder buffer\n");
        netq_node_free(n);
      }
      dtls_info("Added packet for reordering\n");
      return 0;
    } else if (dtls_uint16_to_int(hs_header->message_seq) == peer->handshake_params->hs_state.mseq_r) {
      /* Found the expected packet, use this and all the buffered packet */
      int next = 1;
  
      res = handle_handshake_msg(ctx, peer, session, role, state, data, data_length);
      if (res < 0)
        return res;
  
      /* We do not know in which order the packet are in the list just search the list for every packet. */
      while (next && peer->handshake_params) {
        next = 0;
        netq_t *node = netq_head(peer->handshake_params->reorder_queue);
        while (node) {
          dtls_handshake_header_t *node_header = DTLS_HANDSHAKE_HEADER(node->data);
  
          if (dtls_uint16_to_int(node_header->message_seq) == peer->handshake_params->hs_state.mseq_r) {
            netq_remove(peer->handshake_params->reorder_queue, node);
            next = 1;
            res = handle_handshake_msg(ctx, peer, session, role, peer->state, node->data, node->length);
            if (res < 0) {
              return res;
            }
  
            break;
          } else {
            node = netq_next(node);
          }
        }
      }
      return res;
    }
    wilddog_assert(0, -1);
    return 0;
  }
  
  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;
  
    /* 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_CHANGECIPHERSPEC) {
      dtls_warn("expected ChangeCipherSpec during handshake\n");
      return 0;
    }
  
    if (data_length < 1 || data[0] != 1)
      return dtls_alert_fatal_create(DTLS_ALERT_DECODE_ERROR);
  
    /* Just change the cipher when we are on the same epoch */
    if (peer->role == DTLS_SERVER) {
      err = calculate_key_block(ctx, handshake, peer,
  			      &peer->session, peer->role);
      if (err < 0) {
        return err;
      }
    }
    
    peer->state = DTLS_STATE_WAIT_FINISHED;
  
    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);
  
    dtls_info("** Alert: level %d, description %d\n", data[0], data[1]);
  
    if (!peer) {
      dtls_warn("got an alert for an unknown peer, we probably already removed it, ignore it\n");
      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) {
      dtls_alert("%d invalidate peer\n", data[1]);
      
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
      HASH_DEL_PEER(ctx->peers, peer);
  #else /* WITH_CONTIKI */
      list_remove((void **)&ctx->peers, peer);
  
  #ifndef NDEBUG
      /*PRINTF("removed peer [");
      PRINT6ADDR(&peer->session.addr);
      PRINTF("]:%d\n", 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_send_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_destroy_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) {
        peer->state = DTLS_STATE_CLOSING;
        return dtls_send_alert(ctx, peer, level, desc);
      }
    } else if (err == -1) {
      if (!peer) {
        peer = dtls_get_peer(ctx, session);
      }
      if (peer) {
        peer->state = DTLS_STATE_CLOSING;
        return dtls_send_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 */
      int 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) 
      {
          dtls_debug("dtls_handle_message: PEER NOT FOUND\n");
          dtls_dsrv_log_addr(DTLS_LOG_DEBUG, "peer addr", session);
      } 
      else 
      {
          dtls_debug("dtls_handle_message: FOUND PEER\n");
      }
  
      while ((rlen = is_record(msg,msglen))) 
      {
          dtls_peer_type role;
          dtls_state_t state;
  
          dtls_debug("got packet %d (%d bytes)\n", msg[0], rlen);
          if (peer) 
          {
              data_length = decrypt_verify(peer, msg, rlen, &data);
              if (data_length < 0) 
              {
  	            int err =  dtls_alert_fatal_create(DTLS_ALERT_DECRYPT_ERROR);
                  dtls_info("decrypt_verify() failed\n");
  	            if (peer->state < DTLS_STATE_CONNECTED) 
                  {
                  	dtls_alert_send_from_err(ctx, peer, &peer->session, err);
                  	peer->state = DTLS_STATE_CLOSED;
                  	/* dtls_stop_retransmission(ctx, peer); */
                  	dtls_destroy_peer(ctx, peer, 1);
  	            }
                  return err;
              }
              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_debug_hexdump("receive header", msg, sizeof(dtls_record_header_t));
          dtls_debug_hexdump("receive unencrypted", data, data_length);
  
          /* 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:
                  if (peer) 
                  {
                      dtls_stop_retransmission(ctx, peer);
                  }
                  err = handle_ccs(ctx, peer, msg, data, data_length);
                  if (err < 0) 
                  {
                      dtls_warn("error while handling ChangeCipherSpec message\n");
  	                dtls_alert_send_from_err(ctx, peer, session, err);
  
                  	/* invalidate peer */
                  	dtls_destroy_peer(ctx, peer, 1);
                  	peer = NULL;
  
  	                return err;
                  }
                  break;
  
              case DTLS_CT_ALERT:
                  if (peer) 
                  {
                      dtls_stop_retransmission(ctx, peer);
                  }
                  err = handle_alert(ctx, peer, msg, data, data_length);
                  if (err < 0 || err == 1) 
                  {
                       dtls_warn("received alert, peer has been invalidated\n");
                       /* handle alert has invalidated peer */
                       peer = NULL;
                       return err < 0 ?err:-1;
                  }
                  break;
  
              case DTLS_CT_HANDSHAKE:
                  /* Handshake messages other than Finish must use the current
                   * epoch, Finish has epoch + 1. 
                   */
  
                  if (peer) 
                  {
  	                uint16_t expected_epoch = dtls_security_params(peer)->epoch;
  	                uint16_t msg_epoch = 
  	                dtls_uint16_to_int(DTLS_RECORD_HEADER(msg)->epoch);
  
                  	/* The new security parameters must be used for all messages
                  	 * that are sent after the ChangeCipherSpec message. This
                  	 * means that the client's Finished message uses epoch + 1
                  	 * while the server is still in the old epoch.
                  	 */
  	                if (role == DTLS_SERVER && state == DTLS_STATE_WAIT_FINISHED) 
                      {
  	                    expected_epoch++;
  	                }
  
  	                if (expected_epoch != msg_epoch) 
                      {
  	                    dtls_warn("Wrong epoch, expected %i, got: %i\n",
  		                          expected_epoch, msg_epoch);
  	                    break;
  	                }
                  }
  
                  err = handle_handshake(ctx, peer, session, role, state, data, \
                                         data_length);
                  if (err < 0) 
                  {
  	                dtls_warn("error while handling handshake packet\n");
  	                dtls_alert_send_from_err(ctx, peer, session, err);
  	                return err;
                  }
                  if (peer && peer->state == 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:
                  dtls_info("** application data:\n");
                  if (!peer) 
                  {
                      dtls_warn("no peer available, send an alert\n");
                      //  TODO: should we send a alert here?
                      return -1;
                  }
                  dtls_stop_retransmission(ctx, peer);
                  CALL(ctx, read, &peer->session, data, data_length);
                  break;
              default:
                  dtls_info("dropped unknown message of type %d\n",msg[0]);
          }
  
          /* advance msg by length of ciphertext */
          msg += rlen;
          msglen -= rlen;
      }
  
      return 0;
  }
  
  dtls_context_t *
  dtls_new_context(void *app_data) {
    dtls_context_t *c;
    dtls_tick_t now;
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
    FILE *urandom = fopen("/dev/urandom", "r");
    unsigned char buf[sizeof(unsigned long)];
  #endif /* WITH_CONTIKI */
  
    dtls_ticks(&now);
  #if defined(WITH_CONTIKI) || defined(WILDDOG_PORT_TYPE_WICED) || defined(WILDDOG_PORT_TYPE_QUCETEL) || defined(WILDDOG_PORT_TYPE_MXCHIP)
    /* FIXME: need something better to init PRNG here */
    dtls_prng_init(now);
  #else /* WITH_CONTIKI */
    if (!urandom) {
      dtls_emerg("cannot initialize PRNG\n");
      return NULL;
    }
  
    if (fread(buf, 1, sizeof(buf), urandom) != sizeof(buf)) {
      dtls_emerg("cannot initialize PRNG\n");
      return NULL;
    }
  
    fclose(urandom);
    dtls_prng_init((unsigned long)*buf);
  #endif /* WITH_CONTIKI */
  
    c = malloc_context();
    if (!c)
      goto error;
  
    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 (dtls_prng(c->cookie_secret, DTLS_COOKIE_SECRET_LENGTH))
      c->cookie_secret_age = now;
    else 
      goto error;
  
    return c;
  
   error:
    dtls_alert("cannot create DTLS context\n");
    if (c)
      dtls_free_context(c);
    return NULL;
  }
  
  void
  dtls_free_context(dtls_context_t *ctx) {
    dtls_peer_t *p = NULL;
  
    if (!ctx) {
      return;
    }
  
  
  #if !defined(WITH_CONTIKI) && !defined(WILDDOG_PORT_TYPE_WICED) && !defined(WILDDOG_PORT_TYPE_QUCETEL) && !defined(WILDDOG_PORT_TYPE_MXCHIP)
  
    dtls_peer_t *tmp;
  
    if (ctx->peers) {
      HASH_ITER(hh, ctx->peers, p, tmp) {
        dtls_destroy_peer(ctx, p, 1);
      }
    }
  #else /* WITH_CONTIKI */
  	{
  	  dtls_peer_t *p_next = NULL;
  
  	  for (p = list_head((void **)&ctx->peers); p; p = p_next)
  	  	{
  	  		p_next = list_item_next(p);
  	  		dtls_destroy_peer(ctx, p, 1);
  	  	}
  	}
  #endif /* WITH_CONTIKI */
  
    free_context(ctx);
  }
  
  int
  dtls_connect_peer(dtls_context_t *ctx, dtls_peer_t *peer) {
    int res;
  
    wilddog_assert(peer, -1);
    if (!peer)
      return -1;
  
    /* check if the same peer is already in our list */
    if (peer == dtls_get_peer(ctx, &peer->session)) {
      dtls_debug("found peer, try to re-connect\n");
      return dtls_renegotiate(ctx, &peer->session);
    }
      
    /* set local peer role to client, remote is server */
    peer->role = DTLS_CLIENT;
  
    dtls_add_peer(ctx, peer);
  
    /* send ClientHello with empty Cookie */
    peer->handshake_params = dtls_handshake_new();
        if (!peer->handshake_params)
          return -1;
  
    peer->handshake_params->hs_state.mseq_r = 0;
    peer->handshake_params->hs_state.mseq_s = 0;
    LIST_STRUCT_INIT(peer->handshake_params, reorder_queue);
    res = dtls_send_client_hello(ctx, peer, NULL, 0);
    if (res < 0)
      dtls_warn("cannot send ClientHello\n");
    else 
      peer->state = DTLS_STATE_CLIENTHELLO;
  
    return res;
  }
  
  int
  dtls_connect(dtls_context_t *ctx, const session_t *dst) {
    dtls_peer_t *peer;
    int res;
  
    peer = dtls_get_peer(ctx, dst);
    
    if (!peer)
      peer = dtls_new_peer(dst);
  
    if (!peer) {
      dtls_crit("cannot create new peer\n");
      return -1;
    }
  
    res = dtls_connect_peer(ctx, peer);
  
    /* Invoke event callback to indicate connection attempt or
     * re-negotiation. */
    if (res > 0) {
      CALL(ctx, event, &peer->session, 0, DTLS_EVENT_CONNECT);
    } else if (res == 0) {
      CALL(ctx, event, &peer->session, 0, DTLS_EVENT_RENEGOTIATE);
    }
    
    return res;
  }
  
  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);
        int err;
        unsigned char *data = node->data;
        size_t length = node->length;
        dtls_tick_t now;
        dtls_security_parameters_t *security = dtls_security_params_epoch(node->peer, node->epoch);
  
        dtls_ticks(&now);
        node->retransmit_cnt++;
        node->t = now + (node->timeout << node->retransmit_cnt);
        netq_insert_node(context->sendqueue, node);
        
        if (node->type == DTLS_CT_HANDSHAKE) {
  	dtls_handshake_header_t *hs_header = DTLS_HANDSHAKE_HEADER(data);
  
  	dtls_debug("** retransmit handshake packet of type: %s (%i)\n",
  	           dtls_handshake_type_to_name(hs_header->msg_type), hs_header->msg_type);
        } else {
  	dtls_debug("** retransmit packet\n");
        }
        
        err = dtls_prepare_record(node->peer, security, node->type, &data, &length,
  				1, sendbuf, &len);
        if (err < 0) {
  	dtls_warn("can not retransmit packet, err: %i\n", err);
  	return;
        }
        dtls_debug_hexdump("retransmit header", sendbuf,
  			 sizeof(dtls_record_header_t));
        dtls_debug_hexdump("retransmit unencrypted", node->data, node->length);
  
        (void)CALL(context, write, &node->peer->session, sendbuf, len);
        return;
    }
  
    /* no more retransmissions, remove node from system */
    
    dtls_debug("** removed transaction\n");
  
    /* And finally delete the node */
    netq_node_free(node);
  }
  
  static void
  dtls_stop_retransmission(dtls_context_t *context, dtls_peer_t *peer) {
    netq_t *node;
    node = list_head(context->sendqueue); 
  
    while (node) {
      if (dtls_session_equals(&node->peer->session, &peer->session)) {
        netq_t *tmp = node;
        node = list_item_next(node);
        list_remove(context->sendqueue, tmp);
        netq_node_free(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(context->sendqueue);
  
    dtls_ticks(&now);
    while (node && node->t <= now) {
      netq_pop_first(context->sendqueue);
      dtls_retransmit(context, node);
      node = netq_head(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();
  
    dtls_debug("Started DTLS retransmit process\r\n");
  
    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();
  	if (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 */
  	if (node) {
  	  etimer_set(&the_dtls_context.retransmit_timer, 
  		     node->t <= now ? 1 : node->t - now);
  	} else {
  	  etimer_set(&the_dtls_context.retransmit_timer, 0xFFFF);
  	}
        } 
      }
    }
    
    PROCESS_END();
  }
  #endif /* WITH_CONTIKI */
  